Flutter 功能型組件:顏色和主題(Theme)

前言

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

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

Theme組件能夠爲Material APP定義主題數據(ThemeData)。Material組件庫裏不少組件都使用了主題數據,如導航欄顏色、標題字體、Icon樣式等。Theme內會使用InheritedWidget來爲其子樹共享樣式數據。app

接口描述

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的顏色。less

換膚須要注意的問題

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

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

context.inheritFromWidgetOfExactType 會在widget樹中從當前位置向上查找第一個類型爲_InheritedTheme的widget。因此當局部指定Theme後,其子樹中經過Theme.of()向上查找到的第一個_InheritedTheme即是咱們指定的Theme。
* 想要對整個應用換膚,則能夠去修改MaterialApp的theme屬性。
相關文章
相關標籤/搜索