我一直在寻找一种方法,用于将列表视图中的选定项目永久存储到 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);
}
),
);
}
答案 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>