若是咱们有一小部分咱们想要保存的键值,咱们可使用shared_preferences插件。html
一般咱们不得不编写原平生台集成来存储这两个平台的数据。 幸运的是,shared_preferences插件可用于此目的。 共享偏好设置插件包装iOS上的NSUserDefaults和Android上的SharedPreferences,为简单数据提供持久存储。java
在咱们开始以前,咱们须要将shared_preferences插件添加到咱们的pubspec.yaml文件中:android
dependencies: flutter: sdk: flutter shared_preferences: "<newest version>"
要持久化键值数据,咱们可使用SharedPreferences类。 为了保存数据,咱们调用set方法。 请注意,数据是异步持久的。 若是咱们想要在保存数据时获得通知,请使用commit()函数。缓存
// obtain shared preferences SharedPreferences prefs = await SharedPreferences.getInstance(); // set new value prefs.setInt('counter', counter);
SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1;
在上面的例子中,咱们从counter键加载数据,若是它不存在,则返回0。app
SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.remove('counter');
Setter和getter方法适用于全部原始类。less
虽然使用键值存储很是简单方便,但它有一些限制:异步
有关Android上共享首选项的更多信息,请访问Android开发人员网站上的共享首选项文档。async
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { // This widget is the root of our application. @override Widget build(BuildContext context) { return new MaterialApp( title: 'Shared preferences demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Shared preferences demo'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override void initState() { super.initState(); _loadCounter(); } //Loading counter value on start _loadCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); setState(() { _counter = (prefs.getInt('counter') ?? 0); }); } //Incrementing counter after click _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); _counter = (prefs.getInt('counter') ?? 0) + 1; setState(() { _counter; }); prefs.setInt('counter', _counter); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
经过运行如下代码,咱们能够在咱们的测试中使用初始值填充SharedPreferences:ide
const MethodChannel('plugins.flutter.io/shared_preferences') .setMockMethodCallHandler((MethodCall methodCall) async { if (methodCall.method == 'getAll') { return <String, dynamic>{}; // set initial values here if desired } return null; });
上面的代码应放在测试文件夹下的测试文件中。函数
在某些状况下,将文件读取和写入磁盘可能很是方便。 这可用于跨应用程序启动持续保存数据或从互联网上下载数据并保存以供之后脱机使用。
为了将文件保存到磁盘,咱们须要将path_provider插件与dart:io库结合使用。
路线
在这个例子中,咱们将显示一个计数器。 当计数器发生变化时,咱们须要在磁盘上写入数据,以便在应用程序加载时再次读取它。 所以,咱们须要问:咱们应该在哪里存储这些数据?
path_provider插件提供了一种平台不可知的方式来访问设备文件系统上的经常使用位置。 该插件当前支持访问两个系统文件位置:
在咱们的例子中,咱们但愿将信息存储在文档目录中! 咱们能够像这样找到文档目录的路径:
Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; }
一旦咱们知道在哪里存储文件,咱们须要建立一个文件的完整位置的引用。 咱们可使用dart:io库中的File类来实现此目的。
Future<File> get _localFile async { final path = await _localPath; return new File('$path/counter.txt'); }
如今咱们有一个File可使用,咱们可使用它来读取和写入数据! 首先,咱们将一些数据写入文件。 因为咱们正在使用计数器,所以咱们只会将整数存储为字符串。
Future<File> writeCounter(int counter) async { final file = await _localFile; // Write the file return file.writeAsString('$counter'); }
如今咱们在磁盘上有一些数据,咱们能够阅读它! 再次,咱们将使用File类来完成此操做。
Future<int> readCounter() async { try { final file = await _localFile; // Read the file String contents = await file.readAsString(); return int.parse(contents); } catch (e) { // If we encounter an error, return 0 return 0; } }
为了测试与文件交互的代码,咱们须要模拟对MethodChannel的调用。 MethodChannel是Flutter用来与主机平台进行通讯的类。
在咱们的测试中,咱们没法与设备上的文件系统进行交互。 咱们须要与咱们的测试环境的文件系统进行交互!
为了模拟方法调用,咱们能够在咱们的测试文件中提供一个setupAll函数。 该功能将在测试执行以前运行。
setUpAll(() async { // Create a temporary directory to work with final directory = await Directory.systemTemp.createTemp(); // Mock out the MethodChannel for the path_provider plugin const MethodChannel('plugins.flutter.io/path_provider') .setMockMethodCallHandler((MethodCall methodCall) async { // If we're getting the apps documents directory, return the path to the // temp directory on our test environment instead. if (methodCall.method == 'getApplicationDocumentsDirectory') { return directory.path; } return null; }); });
import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { runApp( new MaterialApp( title: 'Reading and Writing Files', home: new FlutterDemo(storage: new CounterStorage()), ), ); } class CounterStorage { Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } Future<File> get _localFile async { final path = await _localPath; return new File('$path/counter.txt'); } Future<int> readCounter() async { try { final file = await _localFile; // Read the file String contents = await file.readAsString(); return int.parse(contents); } catch (e) { // If we encounter an error, return 0 return 0; } } Future<File> writeCounter(int counter) async { final file = await _localFile; // Write the file return file.writeAsString('$counter'); } } class FlutterDemo extends StatefulWidget { final CounterStorage storage; FlutterDemo({Key key, @required this.storage}) : super(key: key); @override _FlutterDemoState createState() => new _FlutterDemoState(); } class _FlutterDemoState extends State<FlutterDemo> { int _counter; @override void initState() { super.initState(); widget.storage.readCounter().then((int value) { setState(() { _counter = value; }); }); } Future<File> _incrementCounter() async { setState(() { _counter++; }); // write the variable as a string to the file return widget.storage.writeCounter(_counter); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar(title: new Text('Reading and Writing Files')), body: new Center( child: new Text( 'Button tapped $_counter time${_counter == 1 ? '' : 's'}.', ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), ); } }