Flutter/Dart Shared Preferences和设置菜单实例

在 Android Studio/Java 里,Shared Preferences 很容易使用。而在 Flutter/Dart 里,Shared_Preferences就比较让人捉摸不透。

我刚开始学 Shared Preferences 时,我搜索了很多资料。大部分都是讲要创建一个另外的 Class file,或者建立很多很多的 setter/getter 函数。我在想我能不能用另外一种更简单的方法呢。

下面就介绍一下我是怎么做的。

首先向文件 pubspec.yaml 添加 dependency:

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.3 
  
 // TODO: 这里添加 
  shared_preferences: ^0.5.10       
 

然后点 "Pub Get" :

Flutter Shared Preferences Pub get


然后 import package:

import 'package:flutter/material.dart';
//TODO: 添加 package
import 'package:shared_preferences/shared_preferences.dart';
 

 

Flutter/Dart 使用 SharedPreferences 3步骤

1. 初始化

使用 SharedPreferences 的关键就是在 async main() 里创建一个实例,然后在 main() 之前定义一个最高级别变量。之后我们就可以随时随地使用它了。

SharedPreferences mSharedPreferences;

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); //这一行必须有
  mSharedPreferences = await SharedPreferences.getInstance();
  runApp(MyApp());
} 


在这个演示应用中,我们会展示一个气温读数。这个读数的单位是用户选定的,可以是摄氏度C或华氏度F。这个用户偏好就储存在 Shared_Preferences 中。 通常这些数据我们从 API 获得,单位可能是 C ,也可能是 F 。所以展示之前必须转换单位到用户偏好的单位。

接着就是一个设置菜单,用户从这里选择自己要的单位 C 或 F 。这里有两个单位 C 和 F 供选择。你按其中一个字母,那个字母就变成黄色,表示已被选择,同时另一个字母就变为灰色。紧接着就要把这个新选择的值保存到 Shared Preferences,同时主页上的气温数据和单位都要相应变更。

这是整个应用运行的演示图:

Flutter Shared Preferences Settings Menu Demo
 

2. 从 Shared_Preferences 获取数据

每次这个应用开始运行时都会从 SharedPreferrences 读一下用户设定的单位值。如果没有这个值(因为第一次运行),我们就设它为缺省值 "C"。

  var key1='MyUnit';
  var preferredUnit;
  
  @override
  void initState() {
    super.initState();
    preferredUnit = mSharedPreferences.getString(key1) ?? 'C';    
  }       
 

你不用声明或初始化,你就可以去读取任意 KEY 对应的值。你可以读取(并创建)任意多的 KEY-Value Pair 。例如: 

var x = mSharedPreferences.getString(key2); 
var x = mSharedPreferences.getInt('key3'); 
var x = mSharedPreferences.getBool('AnyKey');       
 

当然,如果一个 KEY-Value Pair 从没有创建过,它对应的值是 null 。我们可以这样检查有没有这个 KEY-Value Pair :

  if (mSharedPreferences.getString('KEY')==null) {}
  if (mSharedPreferences.getInt('KEY')==null) {}      
 

 3. 保存值到 Shared_Preferences

用户选取新的温度单位后,我们要把这个值保存到 SharedPreferences 。

 //如果用户按了F
 mSharedPreferences.setString(key1, 'F');       
 
就像读取值时一样,保存值到 Shared Preferences 也不用初始化。但这个保存的操作是一种 "async" 异步操作,有可能不会同步完成。所以如果你马上要用这个值,不要从 Shared Preferences 获取,而是把这个值直接赋给一个变量。
mSharedPreferences.setString(key1, 'F');
//不用马上这样操作:
preferredUnit = mSharedPreferences.getString(key1);
//而是这样做:
preferredUnit = 'F';
 

Flutter/Dart 设置菜单

Flutter/Dart 没有现成的设置菜单。你可以引入一些 packages ,比如:

https://pub.dev/packages/shared_preferences_settings

或者你自己动手,像我这里做的一样。

下面是 main.dart 文件的全部代码,希望你喜欢。

main.dart

import 'package:flutter/material.dart';
//TODO: 引入 package
import 'package:shared_preferences/shared_preferences.dart';

SharedPreferences mSharedPreferences;

void main() async {
  WidgetsFlutterBinding.ensureInitialized(); // 必须有这一行
  mSharedPreferences = await SharedPreferences.getInstance();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final key1 = 'MyUnit'; // Shared_Preferences 的 Key
  final apiTemperature = 20; // 通常从 API 获得这些数据
  final apiUnit = 'C'; // API 的单位可能是 C 也可能是 F,所以要先转换到我们想要的单位值
  var preferredUnit; // 读取用户偏好单位

  @override
  void initState() {
    super.initState();
    // This is how to initialize a Key-Value pair of SharedPreferences.
    // it's value is always null before we set a value to it.
    preferredUnit = mSharedPreferences.getString(key1) ?? 'C';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          PopupMenuButton(
            color: Colors.greenAccent,
            icon: Icon(Icons.settings),
            itemBuilder: (context) => [
              PopupMenuItem(
                value: 1,
                child: StatefulBuilder(
                    //注意:这是把 MenuItem 变成 Stateful, 
                    //它才可以被按后变颜色
                    builder: (BuildContext context, StateSetter setState) {
                  return Row(
                    children: [
                      Column(
                        children: [
                          Text('Choose a unit'),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: [
                              InkWell(
                                onTap: () {
                                  if (preferredUnit == 'F') {
                                    //保存值到 SharedPreferences,
                                    //它是一个 async 异步操作,
                                    // 可能不会同步完成,
                                    mSharedPreferences.setString(key1, 'C');
                                    setState(() {
                                      //所以我们直接把C赋给 preferredUnit 
                                      //而不是从 SharedPreferences 获得
                                      preferredUnit = 'C';
                                    });
                                    //MenuItem 现在在 StatefulBuilder 里面
                                    //所以必须在MyHomePage里再做一个setState来刷新MyHomePage
                                    mySetState('C');
                                  }
                                },
                                child: Container(
                                  color: (preferredUnit == 'C')
                                      ? Colors.yellowAccent
                                      : Colors.black12,
                                  child: Text('°C'),
                                ),
                              ),
                              Container(
                                child: Text('    '),
                              ),
                              InkWell(
                                onTap: () {
                                  if (preferredUnit == 'C') {
                                    mSharedPreferences.setString(key1, 'F');
                                    setState(() {
                                      preferredUnit = 'F';
                                    });
                                    mySetState('F');
                                  }
                                },
                                child: Container(
                                  color: (preferredUnit == 'C')
                                      ? Colors.black12
                                      : Colors.yellowAccent,
                                  child: Text('°F '),
                                ),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ],
                  );
                }),
              ),
              PopupMenuItem(
                // a blank menu item
                child: Text("About"),
                value: 2,
              ),
            ],
            onSelected: (result) {
              if (result == 2) {}
            },
          ),
        ],
        title: Text('Flutter Demo'),
      ),
      body: Center(
        child: Row(
          children: [
            Text('   Today\'s temperature: '),
            Text(
              CFConverter(apiTemperature, apiUnit).toString(),
              style: TextStyle(fontSize: 40, color: Colors.redAccent),
            ),
            Text(
              '°' + preferredUnit,
              style: TextStyle(fontSize: 40, color: Colors.deepOrangeAccent),
            ),
          ],
        ),
      ),
    );
  } //Widget builder end

  void mySetState(String unit) {
    setState(() {
      preferredUnit = unit;
    });
  }

  int CFConverter(int temperature, String unit) {
    if (preferredUnit == apiUnit) {
      return temperature;
    } else if (apiUnit != '' && preferredUnit != apiUnit) {
      if (apiUnit == 'C') {
        return (temperature * 1.8 + 32).round();
      } else if (apiUnit == 'F') {
        return ((temperature - 32) / 1.8).round();
      }
    }
  }
}

       
 


评论