Flutter主題切換——讓你的APP也能一鍵換膚

爲了讓你的 App 更美觀,主題切換已是一個必不可少的功能了,但若是想在傳統的 Android 和 iOS 上分別適配不一樣的主題至關繁瑣。但這一切,在 Flutter 中都很是容易實現。今天咱們就來看看,如何在 Flutter 中給你的 App 添加換膚功能。咱們要實現的效果以下:html

添加依賴

在該案例中,我使用到了 providerflustars 兩個庫,簡單介紹一下這兩個庫:微信

provider

官方推薦的狀態管理庫,相比其餘狀態管理庫使用起來比較方便。app

狀態管理:通俗的講,當咱們想在多個頁面(組件/Widget)之間共享狀態(數據),或者一個頁面(組件/Widget)中的多個子組件之間共享狀態(數據),這個時候咱們就能夠用 Flutter 中的狀態管理來管理統一的狀態(數據),實現不一樣組件直接的傳值和數據共享。less

flustars

號稱「Flutter 全網最全經常使用工具類」,其中包括了SpUtilScreenUtilTimelineUtil等常見工具類,這裏咱們要使用的是SpUtil這個部分,用於存儲用戶所選擇的主題信息。async


以上就是關於咱們使用的兩個第三方庫的介紹,若是想要使用,咱們須要在pubspec.yaml文件中添加以下內容:ide

provider: ^4.0.5
flustars: ^0.2.6+1
複製代碼

準備工做作好了,接下來咱們就開始編碼吧。工具

添加主題樣式

咱們須要先想好本身所須要切換的主題樣式列表,若是以爲麻煩的能夠直接用下面的內容:字體

Map<String, Color> themeColorMap = {
  'gray': Colors.grey,
  'blue': Colors.blue,
  'blueAccent': Colors.blueAccent,
  'cyan': Colors.cyan,
  'deepPurple': Colors.purple,
  'deepPurpleAccent': Colors.deepPurpleAccent,
  'deepOrange': Colors.orange,
  'green': Colors.green,
  'indigo': Colors.indigo,
  'indigoAccent': Colors.indigoAccent,
  'orange': Colors.orange,
  'purple': Colors.purple,
  'pink': Colors.pink,
  'red': Colors.red,
  'teal': Colors.teal,
  'black': Colors.black,
};
複製代碼

使用 Provider 進行全局狀態管理

而後咱們就須要使用 Provider 來進行全局的狀態管理了。首先先建立一個app_provider.dart文件,而後添加以下代碼:ui

class AppInfoProvider with ChangeNotifier {
  String _themeColor = '';

  String get themeColor => _themeColor;

  setTheme(String themeColor) {
    _themeColor = themeColor;
    notifyListeners();
  }
}
複製代碼

由於是全局的狀態管理,接下來咱們須要在main.dart文件中配置一下剛纔建立的 provider,有多個狀態管理就使用 MultiProvider,單個的使用 Provider.value 就好了。(考慮到將來項目的擴展,這裏我就直接使用 MultiProvider)了編碼

class MyApp extends StatelessWidget {
  Color _themeColor;

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider.value(value: AppInfoProvider())],
      child: Consumer<AppInfoProvider>(
        builder: (context, appInfo, _) {
          String colorKey = appInfo.themeColor;
          if (themeColorMap[colorKey] != null) {
            _themeColor = themeColorMap[colorKey];
          }

          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primaryColor: _themeColor,
              floatingActionButtonTheme:
                  FloatingActionButtonThemeData(backgroundColor: _themeColor),
            ),
            home: MyHomePage(title: 'Flutter Theme Change demo'),
          );
        },
      ),
    );
  }
}
複製代碼

若是想要在某個地方改變主題,咱們只須要執行下面這行代碼便可。

Provider.of<AppInfoProvider>(context).setTheme(colorKey);
複製代碼

咱們先來講說上面這段代碼,重點就在於 ThemeData 的設置:

咱們看看ThemeData部分數據定義:

ThemeData({
  Brightness brightness, //深色仍是淺色
  MaterialColor primarySwatch, //主題顏色樣本,見下面介紹
  Color primaryColor, //主色,決定導航欄顏色
  Color accentColor, //次級色,決定大多數Widget的顏色,如進度條、開關等。
  Color cardColor, //卡片顏色
  Color dividerColor, //分割線顏色
  ButtonThemeData buttonTheme, //按鈕主題
  Color cursorColor, //輸入框光標顏色
  Color dialogBackgroundColor,//對話框背景顏色
  String fontFamily, //文字字體
  TextTheme textTheme,// 字體主題,包括標題、body等文字樣式
  IconThemeData iconTheme, // Icon的默認樣式
  TargetPlatform platform, //指定平臺,應用特定平臺控件風格
  ...
})
複製代碼

上面只是ThemeData的一小部分屬性,完整的數據定義讀者能夠查看 SDK。

之因此使用floatingActionButtonTheme單獨設置floatingActionButton而不是使用accentTextTheme,是由於會有警告 ⚠️The support for configuring the foreground color of FloatingActionButtons using ThemeData.accentIconTheme has been deprecated. Please use ThemeData.floatingActionButtonTheme instead. See https://flutter.dev/go/remove-fab-accent-theme-dependency. This feature was deprecated after v1.13.2.意思就是這個屬性將會在1.13.2中被廢棄。不過並不影響咱們如今的使用。

更多關於主題的內容能夠參考 👉顏色和主題

持久化選擇的主題

這裏就須要使用到一開始提到的flustars中的SpUtil了,咱們通常會在頁面初始化加載的時候讀取保存的顏色信息,因此咱們須要在初始化頁面配置以下代碼:

String _colorKey;

@override
void initState() {
  super.initState();
  _initAsync();
}

Future<void> _initAsync() async {
  await SpUtil.getInstance();
  _colorKey = SpUtil.getString('key_theme_color', defValue: 'blue');
  // 設置初始化主題顏色
  Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);
}
複製代碼

await SpUtil.getInstance();這段代碼用於加載SpUtil庫,經過看源碼咱們知道,這個庫採用了單例模式,固然,這不是這篇文章的重點。

上面這段代碼用於初始化主題,咱們經過SpUtil.getString('key_theme_color', defValue: 'blue');獲取保存的主題信息,而後再使用Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);設置主題便可。

初始化主題弄好了,那選擇的代碼又如何編寫呢?

很簡單,只須要才合適的地方調用下面的代碼就能夠了。

setState(() {
  _colorKey = key;
});
SpHelper.putString('key_theme_color', key);
Provider.of<AppInfoProvider>(context).setTheme(key);
複製代碼

思路和上面大同小異,無非是將getString換成了putString

切換主題控件的編寫

上面的代碼提供了切換主題的思路,可是對於用戶來講,他們所要作的是有一個界面可讓他們直接切換主題,所以,下面咱們來編寫切換主題的控件。

由於切換主題一般會在設置界面中出現,因此這裏我用了一個ExpansionTile,這是一個能夠展開的ListTile,代碼以下:

…………
ExpansionTile(
  leading: Icon(Icons.color_lens),
  title: Text('顏色主題'),
  initiallyExpanded: false,
  children: <Widget>[
    Padding(
      padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
      child: Wrap(
        spacing: 8,
        runSpacing: 8,
        children: themeColorMap.keys.map((key) {
          Color value = themeColorMap[key];
          return InkWell(
            onTap: () {
              setState(() {
                _colorKey = key;
              });
              SpUtil.putString('key_theme_color', key);
              Provider.of<AppInfoProvider>(context, listen: false)
                  .setTheme(key);
            },
            child: Container(
              width: 40,
              height: 40,
              color: value,
              child: _colorKey == key
                  ? Icon(
                      Icons.done,
                      color: Colors.white,
                    )
                  : null,
            ),
          );
        }).toList(),
      ),
    )
  ],
),
…………
複製代碼

效果以下:

上面這段代碼就是將咱們最開始選定的一些主題themeColorMap展現出來,告訴用戶能夠切換哪些主題。其中onTap內的代碼就是上一節中提到的設置顏色主題的方法,InkWell主要用於提供主題色的點擊效果,換成GestureDetector也是能夠的。

至此咱們的換膚功能也就完成了,想要獲取完整代碼的能夠關注公衆號「01 二進制」,後臺回覆「Flutter 主題切換」。

最後

以上就是關於如何在 Flutter 中切換主題的詳細內容了。能夠看出,相較於原生應用主題的適配,在 Flutter 中實現換膚的功能簡單不少了。

最後來發布一篇預告,由於 iOS 13 和 Android 10 系統上都新增了「深色模式」,在文中我也提到了ThemeDataBrightness brightness屬性用於表示深色仍是淺色。下一篇文章我就來聊一聊深色模式的適配。

若是你以爲個人文章對你有所幫助,不妨給個贊 👍 或者關注支持一下。

黑白大氣神祕簡約微信公衆號二維碼
相關文章
相關標籤/搜索