【Flutter】功能型組件之顏色和主題


前言

Color類中顏色以一個int值保存,顯示器顏色是由紅、綠、藍三基色組成,每種顏色占8比特,存儲結構如下:

Bit(位) 顏色
0-7 藍色
8-15 綠色
16-23 紅色
24-31 Alpha(不透明度)

Theme組件可以為Material APP定義主題數據(ThemeData)。Material組件庫里很多組件都使用了主題數據,如導航欄顏色、標題字體、Icon樣式等。Theme內會使用InheritedWidget來為其子樹共享樣式數據。

接口描述

factory ThemeData({
    // 深色還是淺色
    Brightness brightness,
    // 主題顏色樣本
    MaterialColor primarySwatch,
    // 主色,決定導航欄顏色
    Color primaryColor,
    Brightness primaryColorBrightness,
    Color primaryColorLight,
    Color primaryColorDark,
    // 次級色,決定大多數Widget的顏色,如進度條、開關等
    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,
    // 字體主題,包括標題、body等文字樣式
    TextTheme textTheme,
    TextTheme primaryTextTheme,
    TextTheme accentTextTheme,
    InputDecorationTheme inputDecorationTheme,
    // Icon的默認樣式
    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,
    Typography typography,
    CupertinoThemeData cupertinoOverrideTheme,
    SnackBarThemeData snackBarTheme,
    BottomSheetThemeData bottomSheetTheme,
    PopupMenuThemeData popupMenuTheme,
    MaterialBannerThemeData bannerTheme,
    DividerThemeData dividerTheme,
    ButtonBarThemeData buttonBarTheme,
  }) {
    brightness ??= Brightness.light;
    final bool isDark = brightness == Brightness.dark;
    primarySwatch ??= Colors.blue;
    primaryColor ??= isDark ? Colors.grey[900] : primarySwatch;
    primaryColorBrightness ??= estimateBrightnessForColor(primaryColor);
    primaryColorLight ??= isDark ? Colors.grey[500] : primarySwatch[100];
    primaryColorDark ??= isDark ? Colors.black : primarySwatch[700];
    final bool primaryIsDark = primaryColorBrightness == Brightness.dark;
    toggleableActiveColor ??= isDark ? Colors.tealAccent[200] : (accentColor ?? primarySwatch[600]);
    accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500];
    accentColorBrightness ??= estimateBrightnessForColor(accentColor);
    final bool accentIsDark = accentColorBrightness == Brightness.dark;
    canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
    scaffoldBackgroundColor ??= canvasColor;
    bottomAppBarColor ??= isDark ? Colors.grey[800] : Colors.white;
    cardColor ??= isDark ? Colors.grey[800] : Colors.white;
    dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);

代碼示例

// 顏色和主題(Theme)


import 'package:flutter/material.dart';

// 實現一個背景顏色和Title可以自定義的導航欄,並且背景色為深色時我們應該讓Title顯示為淺色;背景色為淺色時,Title顯示為深色。
class NavBar extends StatelessWidget{
  final String title;
  // 背景顏色
  final Color color;

  NavBar({
    Key key,
    this.color,
    this.title,
  });

  @override
  Widget build(BuildContext context){
    return Container(
      constraints: BoxConstraints(
        minHeight: 52,
        minWidth: double.infinity,
      ),
      decoration: BoxDecoration(
        color: color,
        boxShadow: [
          // 陰影
          BoxShadow(
            color: Colors.black26,
            offset: Offset(0, 3),
            blurRadius: 3,
          ),
        ],
      ),
      child: Text(
        title,
        style: TextStyle(
          fontWeight: FontWeight.bold,
          // 根據背景色亮度來確定Title顏色
          color: color.computeLuminance() < 0.5 ? Colors.white: Colors.black,
        ),
      ),
      alignment: Alignment.center,
    );
  }

}

class NavBarTest extends StatelessWidget{

  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: Text("顏色"),
      ),
      body: Column(
        children: <Widget>[
          // 背景色為藍色,則title自動為白色
          NavBar(color: Colors.blue, title: "標題",),
          // 背景色為白色,則title自動為黑色
          NavBar(color: Colors.white, title: "標題",)
        ],
      )
    );
  }
}


// 路由換膚
class ThemeTest extends StatefulWidget{

  @override
  _ThemeTestState createState() => _ThemeTestState();
}

class _ThemeTestState extends State<ThemeTest>{
  // 當前路由主顏色
  Color _themeColor = Colors.teal;

  @override
  Widget build(BuildContext context){
    ThemeData themeData = Theme.of(context);

    return Theme(
      data: ThemeData(
        // 用於導航欄、FloatingActionButton的背景色
        primarySwatch: _themeColor,
        // 用於Icon顏色
        iconTheme: IconThemeData(color: _themeColor),
      ),
      child: Scaffold(
        appBar: AppBar(
          title: Text("主題測試"),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // 第一行Icon使用主題中的iconTheme
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Icon(Icons.favorite),
                Icon(Icons.airport_shuttle),
                Text("顏色跟隨主題"),
              ],
            ),
            // 為第二行Icon自定義顏色,固定為黑色
            Theme(
              data: themeData.copyWith(
                iconTheme: themeData.iconTheme.copyWith(
                  color: Colors.black,
                ),
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Icon(Icons.favorite),
                  Icon(Icons.airport_shuttle),
                  Text("顏色固定黑色")
                ],
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          // 切換主題
          onPressed: () => setState(
                () => _themeColor = _themeColor == Colors.teal ? Colors.blue: Colors.teal,
          ),
          child: Icon(Icons.palette),
        ),
      ),
    );
  }
}



總結

將顏色字符串轉成Color對象

Color(0xffdc380d); //如果顏色固定可以直接使用整數值
//顏色是一個字符串變量
var c = "dc380d";
Color(int.parse(c,radix:16)|0xFF000000) //通過位運算符將Alpha設置為FF
Color(int.parse(c,radix:16)).withAlpha(255)  //通過方法將Alpha設置為FF

MaterialColor

MaterialColor是實現Material Design中的顏色的類,它包含一種顏色的10個級別的漸變色。MaterialColor通過"[]"運算符的索引值來代表顏色的深度,有效的索引有:50,100,200,…,900,數字越大,顏色越深。MaterialColor的默認值為索引等於500的顏色。

換膚需要注意的問題

  • 可以通過局部主題覆蓋全局主題,正如代碼中通過Theme為第二行圖標指定固定顏色(黑色)一樣,這是一種常用的技巧,Flutter中會經常使用這種方法來自定義子樹主題。那么為什么局部主題可以覆蓋全局主題?這主要是因為widget中使用主題樣式時是通過Theme.of(BuildContext context)來獲取的。

static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
// 簡化代碼,並非源碼
return context.inheritFromWidgetOfExactType(_InheritedTheme).theme.data
}

context.inheritFromWidgetOfExactType 會在widget樹中從當前位置向上查找第一個類型為_InheritedTheme的widget。所以當局部指定Theme后,其子樹中通過Theme.of()向上查找到的第一個_InheritedTheme便是我們指定的Theme。
* 想要對整個應用換膚,則可以去修改MaterialApp的theme屬性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM