Flutter Web網站之最簡方式實現暗黑主題無縫切換

i校長 發表於 2020-05-09

往期

上期回顧

上期我們做了優化,主要針對ScrollView+GridView的使用場景,用了更加合適的元件,這期想做一個主題變更,為什麼呢?

  • 第一 暗黑主題越來越剛需化,現在哪個主流App沒有暗黑都不好意思上架,而ios陣營更加強硬的要求平臺實現,否則下架,庫克牛逼,我們惹不起。
  • 第二 專案還處於初期,這個時候重構改動成本較低
  • 第三 主題的變更網上有很多框架可以快速實現,但我想尋求的是最簡單的實現,不想引入別人的框架,一來自己瞭如指掌,二來不用依賴別人的升級來滿足未來奇葩的需求。 這期實現其實很簡單,來往下看。

實現效果

大屏淺色

Flutter Web網站之最簡方式實現暗黑主題無縫切換
大屏暗黑
Flutter Web網站之最簡方式實現暗黑主題無縫切換
小屏淺色
Flutter Web網站之最簡方式實現暗黑主題無縫切換
小屏暗黑
Flutter Web網站之最簡方式實現暗黑主題無縫切換

程式碼實現

定義主題類AppTheme,用來配置不同的ThemeData

class AppTheme {

  ThemeData themeData;

  AppTheme(this.themeData);

  // ignore: non_constant_identifier_names
  static final AppTheme DARK_THEME = AppTheme(ThemeData.dark());

  // ignore: non_constant_identifier_names
  static final AppTheme LIGHT_THEME = AppTheme(
      ThemeData(brightness: Brightness.light, primaryColor: Colors.grey[50]));
}
複製程式碼

DARK_THEME暗黑主題 LIGHT_THEME淺色主題、淺色標題欄預設藍色,這裡我改成淺灰色

定義StreamController,用來動態變更資料

class ThemeBloc {
  // ignore: close_sinks
  final _themeStreamController = StreamController<AppTheme>();
  /// 變更主題呼叫方法
  get changeTheTheme => _themeStreamController.sink.add;
  /// 主題資料
  get themeData => _themeStreamController.stream;
}

final bloc = ThemeBloc();
複製程式碼

給原來的MaterialApp包一層StreamBuilder

 StreamBuilder<AppTheme>(
        initialData: AppTheme.LIGHT_THEME,
        stream: bloc.themeData,
        builder: (context, AsyncSnapshot<AppTheme> snapshot) {
          return MaterialApp(
            title: 'Jetpack',
            theme: snapshot.data.themeData,
            home: PageHome(),
            routes: <String, WidgetBuilder>{
              "/qq": (context) => PageQQLink(),
              "/pageChatGroup": (context) => PageChatGroup(),
            },
          );
        })
複製程式碼

initialData 預設資料 stream 將bloc.themeData賦值給它,用來監聽資料變化 snapshot 資料變化的快照,最終交給MaterialApp的theme,從而實現動態變更。如何觸發變更資料呢?

Flutter Web網站之最簡方式實現暗黑主題無縫切換
如圖設定頁面新增了開關,程式碼如下

SwitchListTile(
          secondary: Icon(_isEnabled ? Icons.brightness_4 : Icons.brightness_5),
          title: Text("暗黑主題"),
          subtitle: Text("將主題調成暗黑色"),
          value: _isEnabled,
          onChanged: (value) {
            setState(() {
              _isEnabled = value;
            });
            if (value) {
              bloc.changeTheTheme(AppTheme.DARK_THEME);
            } else {
              bloc.changeTheTheme(AppTheme.LIGHT_THEME);
            }
          },
        )
複製程式碼

這裡呼叫bloc.changeTheTheme來變更主題。 對變更主題就是這麼簡單,你是不是有疑問,我如何修改其他主題呢(如:文字,按鈕,對話方塊等)

ThemeData

再看下我得實現

  static final AppTheme LIGHT_THEME = AppTheme(
      ThemeData(brightness: Brightness.light, primaryColor: Colors.grey[50]));
複製程式碼

我這裡修改了brightness、primaryColor,其實它還有很多,請看

 ThemeData({
    Brightness brightness,
    VisualDensity visualDensity,
    MaterialColor primarySwatch,
    Color primaryColor,
    Brightness primaryColorBrightness,
    Color primaryColorLight,
    Color primaryColorDark,
    Color accentColor,
    Brightness accentColorBrightness,
    Color canvasColor,
    Color scaffoldBackgroundColor,
    Color bottomAppBarColor,
    Color cardColor,
    Color dividerColor,
    Color focusColor,
    Color hoverColor,
    Color highlightColor,
    Color splashColor,
    InteractiveInkFeatureFactory splashFactory,
    Color selectedRowColor,
    Color unselectedWidgetColor,
    Color disabledColor,
    Color buttonColor,
    ButtonThemeData buttonTheme,
    ToggleButtonsThemeData toggleButtonsTheme,
    Color secondaryHeaderColor,
    Color textSelectionColor,
    Color cursorColor,
    Color textSelectionHandleColor,
    Color backgroundColor,
    Color dialogBackgroundColor,
    Color indicatorColor,
    Color hintColor,
    Color errorColor,
    Color toggleableActiveColor,
    String fontFamily,
    TextTheme textTheme,
    TextTheme primaryTextTheme,
    TextTheme accentTextTheme,
    InputDecorationTheme inputDecorationTheme,
    IconThemeData iconTheme,
    IconThemeData primaryIconTheme,
    IconThemeData accentIconTheme,
    SliderThemeData sliderTheme,
    TabBarTheme tabBarTheme,
    TooltipThemeData tooltipTheme,
    CardTheme cardTheme,
    ChipThemeData chipTheme,
    TargetPlatform platform,
    MaterialTapTargetSize materialTapTargetSize,
    bool applyElevationOverlayColor,
    PageTransitionsTheme pageTransitionsTheme,
    AppBarTheme appBarTheme,
    BottomAppBarTheme bottomAppBarTheme,
    ColorScheme colorScheme,
    DialogTheme dialogTheme,
    FloatingActionButtonThemeData floatingActionButtonTheme,
    NavigationRailThemeData navigationRailTheme,
    Typography typography,
    CupertinoThemeData cupertinoOverrideTheme,
    SnackBarThemeData snackBarTheme,
    BottomSheetThemeData bottomSheetTheme,
    PopupMenuThemeData popupMenuTheme,
    MaterialBannerThemeData bannerTheme,
    DividerThemeData dividerTheme,
    ButtonBarThemeData buttonBarTheme,
  }
複製程式碼

這裡面得主題你都可以修改,你是不是看到了 AppBarTheme、DialogTheme、TextTheme、BottomSheetThemeData等等,不用我解釋了吧,你應該知道是誰得樣式了吧。

總結

20幾行新增得程式碼就搞定了,為什麼還要用別人得框架呢?是吧。

囉嗦一句

計劃真的是趕不上變化,不急,慢慢完善哈。

原始碼

請轉github程式碼檢視完整實現

ToDo

該部分內容後期慢慢給大家更新,請客觀不要著急,當然你可以參與進來,私聊我就行哦。

  • 示例部準備下期實現,跳轉至詳情頁面,並展示用例。原始碼已經完成點選即可跳轉至github。
  • Tags 部分下期實現,這部分也需要新的UI展現,標籤的功能類似與搜尋,提供更快捷的方式查詢想要的功能。
  • 留言功能設計,在你們使用過程中肯定會有不同的建議,有了這個功能就能知道你們的心聲,所以這也是我們需要的實現的一個功能。
  • 優秀的專案推薦,有很多優秀的專案等待著我們去發現,我一個人的能力有限,如果有更多的人來推薦,會不斷豐富我們的Jetpack內容。
  • 部落格,這裡考慮到有很多優秀的大佬,寫過相關技術部落格,幫你尋找最優秀的資源。功能設計如下圖新增按鈕。
    Flutter Web網站之最簡方式實現暗黑主題無縫切換

結束

網站jetpack.net.cn,歡迎常來,也希望能在你學習Flutter的道路上提供一丟丟的幫助。