Flutter-如何仅在列表视图中显示选定的项目

时间:2018-07-20 21:53:23

标签: android ios variables dart flutter

我一直在寻找一种方法,用于将列表视图中的选定项目永久存储到 Flutter / Dart (飞镖/飞镖)中。

该问题包含完整的代码,项目中包含更多内容,可以在以下网址查看:https://github.com/Jak3-02/myproject2

任何对此的想法将不胜感激。

主列表视图位于home_page.dart

带有已保存项目的第二个列表视图位于favourites_page.dart

这是有问题的主列表视图:

Widget _cryptoWidget() {
    return new Container(
        child: new Column(
          children: <Widget>[
            new Flexible(
              child: new ListView.builder(
                itemCount: _currencies.length,
                itemBuilder: (BuildContext context, int index) {
                  final int i = index ~/ 2;
                  final Crypto currency = _currencies[i];
                  final MaterialColor color = _colors[i % _colors.length];
                  if (index.isOdd) {
                    return new Divider();
                  }
                  return _getListItemUi(currency, color);
                },
              ),
            ),
          ],
        )
      );
  }

  ListTile _getListItemUi(Crypto currency, MaterialColor color) {
    return new ListTile(
      leading: new Image.network("http://cryptoicons.co/32@2x/color/"+currency.symbol.toLowerCase()+"@2x.png"),
      title: new Text(currency.name,
          style: new TextStyle(fontWeight: FontWeight.bold)),
      subtitle:
      _getSubtitleText(currency.price_usd, currency.percent_change_1h),
      isThreeLine: true,
      trailing: new IconButton(
        icon: new Icon(Icons.add),
        onPressed: () async { Directory appDocDir = await         getApplicationDocumentsDirectory();
        String appDocPath = appDocDir.path;
        var myFile = new File('$appDocPath/my_file.txt')
        ..writeAsStringSync('myVar: $_currencies');  
        print(myFile.absolute.path);
        }
      ),
    );
  }

1 个答案:

答案 0 :(得分:3)

我很抱歉-这变成了文字= D的墙。希望它会有所帮助!

在混乱中,数据几乎总是沿着所谓的“小部件树”传递。这部分是为了提高性能,但似乎也是开发人员首选的范例。

这意味着,如果您有两个不同的“页面”,每个页面都与导航器一起显示,则在它们之间共享状态通常并不简单。

但是,有一个例外-如果您始终要从“主页”页面转到“收藏夹”页面,则可以推送包含数据的MaterialPageRoute。就像这样-直接从flutter getting started codelab复制:

final _saved = Set<WordPair>()

....

Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) {
        // instead of whatever they're doing here, you'd
        // parse your data and pass it in to your page.
        final tiles = _saved.map(
          (pair) {
            return ListTile(
              title: Text(
                pair.asPascalCase,
                style: _biggerFont,
              ),
            );
          },
        );
        final divided = ListTile
          .divideTiles(
            context: context,
            tiles: tiles,
          )
          .toList();

        return Scaffold(
          appBar: AppBar(
            title: Text('Saved Suggestions'),
          ),
          body: ListView(children: divided),
        );
      },
    ),
  );

此方法的警告是,如果您想从非“主”页面的任何地方直接转到“收藏夹”页面,则不会。

如果这是一个问题,则实际上您需要从上向下传递信息。可以通过多种方法进行操作-我将对每种方法进行解释并提供一些建议。

1:简单方法

最简单的方法是简单地将数据(即收藏夹列表或收藏夹标识符)保存在导航器级别的有状态小部件中,并在构造它们时将信息传递到页面中。然后,您需要在有状态窗口小部件上创建一个of函数(请参见Scaffold's of-这是一种常见的波动模式),并具有一个设置此窗口小部件状态的函数(您将要设置列表)最喜欢的。)

此方法存在一些问题:

  • 您可能必须将数据向下传递到多个级别的小部件中,这对于代码的可维护性而言不是很重要
  • 每次添加收藏夹时,都会在有状态的小部件下重建一切,这可能会降低运行速度。

2:稍微优雅一点,但还没有遵循一种模式

执行此操作的中间方法是将InheritedWidget和StatefulWidget一起使用。您必须将继承的窗口小部件放置在树中页面上方的某个位置-导航器上方是您最安全的选择(如果您的应用程序内部使用了Navigator,则它应该是MaterialApp,如果您的应用程序只是使用它的话)。

如果继承的窗口小部件在窗口小部件树中,则可以像上一个示例中的statefulWidget一样使用of方法对其进行访问。这是直接来自InheritedWidget docs的示例:

class FrogColor extends InheritedWidget {
  const FrogColor({
    Key key,
    @required this.color,
    @required Widget child,
  }) : assert(color != null),
       assert(child != null),
       super(key: key, child: child);

  final Color color;

  static FrogColor of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(FrogColor);
  }

  @override
  bool updateShouldNotify(FrogColor old) => color != old.color;
}

通过使用InheritedWidget,只要继承的窗口小部件发生更改,您的窗口小部件就会重建。但是,这不是InheritedWidget的数据更改时-实际替换时。 InheritedData的事情是它是不可变的(即所有成员必须是最终成员),并且仍然无助于将状态传递回树。

因此,您仍然需要一个StatefulWidget,以便您可以通过收藏夹列表。这归结为由StatefulWidget构建一个InheritedWidget,使用InheritedWidget.of读取数据,并使用StatefulWidget.of设置状态。有关如何进行此连接的示例,请参见this tutorial

缺点是这样做的样板很多。

2.5:让我们简化一下=)

您不必使用任何继承的窗口小部件,而是可以使用诸如the scoped_model plugin之类的帮助。它使您可以简单地编写模型(从范围模型继承)。尽管如此,它仍然使用基本相同的底层构造。

以下是直接来自scoped_model插件的示例:

// Start by creating a class that holds some view the app's state. In
// our example, we'll have a simple counter that starts at 0 can be 
// incremented.
//
// Note: It must extend from Model.  
class CounterModel extends Model {
  int _counter = 0;

  int get counter => _counter;

  void increment() {
    // First, increment the counter
    _counter++;

    // Then notify all the listeners.
    notifyListeners();
  }
}

// Create our App, which will provide the `CounterModel` to 
// all children that require it! 
class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // First, create a `ScopedModel` widget. This will provide 
    // the `model` to the children that request it. 
    return new ScopedModel<CounterModel>(
      model: new CounterModel(),
      child: new Column(children: [
        // Create a ScopedModelDescendant. This widget will get the
        // CounterModel from the nearest ScopedModel<CounterModel>. 
        // It will hand that model to our builder method, and rebuild 
        // any time the CounterModel changes (i.e. after we 
        // `notifyListeners` in the Model). 
        new ScopedModelDescendant<CounterModel>(
                builder: (context, child, model) => new Text(
                    model.counter.toString()),
              ),
        new Text("Another widget that doesn't depend on the CounterModel")
      ])
    );
  }
}

它们在ScopedModel代码中有一些棘手的事情,因此可以加以改进。但这可能是执行所需操作的最简单的代码方式。

3:完善的设计模式-BLoC

继续我们一直遵循的轨迹,逻辑上的下一个选择是遵循一种设计模式,该模式包含了我们一直在谈论的某些元素,但是用更多的逻辑将其包裹起来。一种这样的模式是BLoC(_B_usiness _Lo_gic _C_omponent),它是由Google设计的,具有相当简单的抖动实现。本质上,使用此功能,您正在制作一个处理事物的逻辑组件-例如,它实际上可以处理检索项目列表以及保存收藏夹。您可以使用InheritedWidget或ScopedModel来访问组件。

由于我无法解释BLoC,因此我将在这里不做更多详细介绍,但是您可以查看this flutter specific example。这种方法的优点是您的组件是独立于平台的,因此您以后可以在其他上下文中使用它们,例如AngularDart。

这样做的缺点是,您仍然必须决定如何实施所有事情,因为对此还没有意见,而且仍然需要大量样板。

3.5:不同的设计模式-Redux

您可以采用的另一个范例是Redux。我相信它起源于React,但是有一个颤动的端口。它本质上将所有内容都视为状态,然后将状态传播到需要的状态。这样做的好处是,您可以放置​​一个状态适配器来执行诸如将数据持久保存到数据库之类的事情,而无需更改其余的yoru代码。

您关于redux的最佳信息来源是its flutter plugin。我个人不太喜欢它,因为我觉得它对应该做什么和不应该做什么有太多规则,并且需要很多样板,但是每个样板都有。

保留数据

尽管上述选项涉及如何在正在运行的实例中传递数据,但实际上并没有涉及如何持久化数据。对于2和2.5,您基本上必须在逻辑中放入一些东西,以将数据加载并保存到某处-flutter通过SQFlite插件对SQLite具有良好的支持,可以使用SharedPreferences,也可以使用Firebase数据库之一(取决于您需要保存多少个收藏夹!)。

如果您正在使用BLoC,则本质上是希望将存储机制注入到组件中(以保持组件与平台无关),而在Redux中,您可以使用redux_persist来持久存储。 / p>