若是咱們有一小部分咱們想要保存的鍵值,咱們可使用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), ), ); } }