Flutter主題風格

coderwhy發表於2020-06-02

在Flutter開發中,我們可以通過定義 Theme,複用顏色和字型樣式,從而讓整個app的設計看起來更一致。

一. Theme主題的使用

Theme分為:全域性Theme和區域性Theme

主題有兩個作用:

  • 設定了主題之後,某些Widget會自動使用主題的樣式(比如AppBar的顏色)
  • 將某些樣式放到主題中統一管理,在應用程式的其它地方直接引用

1.1. 全域性Theme

全域性Theme會影響整個app的顏色和字型樣式。

使用起來非常簡單,只需要向MaterialApp構造器傳入 ThemeData 即可。

  • 如果沒有設定Theme,Flutter將會使用預設的樣式。
  • 當然,我們可以對它進行定製。
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // 1.亮度: light-dark
        brightness: Brightness.light,
        // 2.primarySwatch: primaryColor/accentColor的結合體
        primarySwatch: Colors.red,
        // 3.主要顏色: 導航/底部TabBar
        primaryColor: Colors.pink,
        // 4.次要顏色: FloatingActionButton/按鈕顏色
        accentColor: Colors.orange,
        // 5.卡片主題
        cardTheme: CardTheme(
          color: Colors.greenAccent,
          elevation: 10,
          shape: Border.all(width: 3, color: Colors.red),
          margin: EdgeInsets.all(10)
        ),
        // 6.按鈕主題
        buttonTheme: ButtonThemeData(
          minWidth: 0,
          height: 25
        ),
        // 7.文字主題
        textTheme: TextTheme(
          title: TextStyle(fontSize: 30, color: Colors.blue),
          display1: TextStyle(fontSize: 10),
        )
      ),
      home: HYHomePage(),
    );
  }
}
複製程式碼
全域性主題
全域性主題

1.2. 區域性Theme

如果某個具體的Widget不希望直接使用全域性的Theme,而希望自己來定義,應該如何做呢?

  • 非常簡單,只需要在Widget的父節點包裹一下Theme即可

建立另外一個新的頁面,頁面中使用新的主題:

  • 在新的頁面的Scaffold外,包裹了一個Theme,並且設定data為一個新的ThemeData
class HYSecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Theme(
      data: ThemeData(),
      child: Scaffold(
      ),
    );
  }
}
複製程式碼
新的主題
新的主題

但是,我們很多時候並不是想完全使用一個新的主題,而且在之前的主題基礎之上進行修改:

class HYSecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Theme(
      data: Theme.of(context).copyWith(
        primaryColor: Colors.greenAccent
      ),
      child: Scaffold(
      ),
    );
  }
}
複製程式碼

1.3. Flutter中文網錯誤

但是這裡有一個注意事項:accentColor在這裡並不會被覆蓋。

為什麼不能覆蓋呢?https://github.com/material-components/material-components-flutter-codelabs/issues/106

我摘抄一點官方人員的回覆:

image-20200325105126901
image-20200325105126901

其實官網文件中之前也出現了類似的錯誤,比如Flutter中文網之前是翻譯官方文件的

  • https://flutterchina.club/cookbook/design/themes/其中就有該錯誤
image-20200325103731257
image-20200325103731257

後來官網文件中對這個問題進行了修正:

image-20200325103957271
image-20200325103957271

二. 暗黑Theme適配

2.1. darkTheme

目前很多應用程式都需要適配暗黑模式,Flutter中如何做到暗黑模式的適配呢?

事實上,MaterialApp中有theme和dartTheme兩個引數:

  • 按照下面的寫法,我們已經預設適配了暗黑主題
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: HYHomePage(),
    );
  }
}
複製程式碼
主題適配
主題適配

2.2. 開發中適配

在開發中,為了能適配兩種主題(設定是更多的主題),我們可以封裝一個AppTheme

  • 1.公共的樣式抽取成常量
  • 2.封裝一個亮色主題
  • 3.封裝一個暗黑主題
import 'package:flutter/material.dart';

class AppTheme {
  // 1.抽取相同的樣式
  static const double _titleFontSize = 20;
  
  // 2.亮色主題
  static final ThemeData lightTheme = ThemeData(
    primarySwatch: Colors.pink,
    primaryTextTheme: TextTheme(
      title: TextStyle(
        color: Colors.yellow,
        fontSize: _titleFontSize
      )
    ),
    textTheme: TextTheme(
      body1: TextStyle(color: Colors.red)
    )
  );
  
  // 3.暗黑主題
  static final ThemeData darkTheme = ThemeData(
    primaryColor: Colors.grey,
    primaryTextTheme: TextTheme(
      title: TextStyle(
        color: Colors.white,
        fontSize: _titleFontSize
      )
    ),
    textTheme: TextTheme(
      title: TextStyle(color: Colors.white),
      body1: TextStyle(color: Colors.white70)
    )
  );
}
複製程式碼

在MaterialApp中,可以決定使用哪一個主題:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      home: HYHomePage(),
    );
  }
}
複製程式碼

備註:所有內容首發於公眾號,之後除了Flutter也會更新其他技術文章,TypeScript、React、Node、uniapp、mpvue、資料結構與演算法等等,也會更新一些自己的學習心得等,歡迎大家關注

公眾號
公眾號

相關文章