本实例APP中语言和主题都是可以设置的,而两者都是通过ChangeNotifierProvider来实现的:我们在main函数中使用了Consumer2,依赖了ThemeModel和LocaleModel,因此,当我们在语言和主题设置页更该当前的配置后,Consumer2的builder都会重新执行,构建一个新的MaterialApp,所以修改会立即生效。下面看一下语言和主题设置页的实现

语言选择页

APP语言选择页提供三个选项:中文简体、美国英语、跟随系统。我们将当前APP使用的语言高亮显示,并且在后面添加一个“对号”图标,新建 lib/routes/LanguageRoute.dart 文件,实现如下:

  1. class LanguageRoute extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. var color = Theme.of(context).primaryColor;
  5. var localeModel = Provider.of<LocaleModel>(context);
  6. var gm = GmLocalizations.of(context);
  7. //构建语言选择项
  8. Widget _buildLanguageItem(String lan, value) {
  9. return ListTile(
  10. title: Text(
  11. lan,
  12. // 对APP当前语言进行高亮显示
  13. style: TextStyle(color: localeModel.locale == value ? color : null),
  14. ),
  15. trailing:
  16. localeModel.locale == value ? Icon(Icons.done, color: color) : null,
  17. onTap: () {
  18. // 更新locale后MaterialApp会重新build
  19. localeModel.locale = value;
  20. },
  21. );
  22. }
  23. return Scaffold(
  24. appBar: AppBar(
  25. title: Text(gm.language),
  26. ),
  27. body: ListView(
  28. children: <Widget>[
  29. _buildLanguageItem("中文简体", "zh_CN"),
  30. _buildLanguageItem("English", "en_US"),
  31. _buildLanguageItem(gm.auto, null),
  32. ],
  33. ),
  34. );
  35. }
  36. }

上面代码逻辑很简单,唯一需要注意的是我们在build(…)方法里面定义了_buildLanguageItem(…)方法,它和在LanguageRoute类中定义该方法的区别就在于:在build(…)内定义的方法可以共享build(…)方法上下文中的变量,本例中是共享了localeModel。当然,如果_buildLanguageItem(…)的实现复杂一些的话不建议这样做,此时最好是将其作为LanguageRoute类的方法。该页面运行效果如图所示:
多语言和多主题 - 图1多语言和多主题 - 图2
切换语言后立即生效

主题选择页

一个完整的主题Theme包括很多选项,这些选项在ThemeData中定义。本实例为了简单起见,我们只配置主题颜色。我们提供几种默认预定义的主题色供用户选择,用户点击一种色块后则更新主题。
新建 lib/routes/ThemeChangeRoute.dart 文件,主题选择页的实现代码如下:

  1. class ThemeChangeRoute extends StatelessWidget{
  2. @override
  3. Widget build(BuildContext context) {
  4. return Scaffold(
  5. appBar: AppBar(
  6. title: Text(GmLocalizations.of(context).theme),
  7. ),
  8. body: ListView( //显示主题色块
  9. children: Global.themes.map<Widget>((e) {
  10. return GestureDetector(
  11. child: Padding(
  12. padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16),
  13. child: Container(
  14. color: e,
  15. height: 40,
  16. ),
  17. ),
  18. onTap: () {
  19. //主题更新后,MaterialApp会重新build
  20. Provider.of<ThemeModel>(context).theme = e;
  21. },
  22. );
  23. }).toList(),
  24. ),
  25. );
  26. }
  27. }

运行效果如图所示:
多语言和多主题 - 图3
点击其它主题色块后,APP主题色立马切换生效