Flutter 功能型组件:颜色和主题(Theme)

Stella981
• 阅读 1155

前言

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属性。
点赞
收藏
评论区
推荐文章
布袋罗汉 布袋罗汉
1年前
BetterDisplay Pro for Mac(虚拟显示器)
BetterDisplay是一款适用于Mac的显示器校准软件,可帮助用户调整显示器的颜色、亮度和对比度设置,以获得准确一致的颜色。该软件旨在通过优化显示参数来提高图像和视频的视觉质量。注意:多双击几次可正常打开,如果双击app不能打开软件的话,可以右键Be
Python进阶者 Python进阶者
3年前
一篇文章带你了解CSS 文本样式
大家好,我是IT共享者,人称皮皮。这篇文章我们来讲讲CSS的文本样式。一、文本颜色Color颜色属性被用来设置文字的颜色。颜色是通过CSS最经常的指定:十六进制值如"#FF0000"。一个RGB值"RGB(255,0,0)"。颜色的名称如"红"。一个网页的文本颜色是指在主体内的选择:
Stella981 Stella981
3年前
C# WinForm窗体控件Panel修改边框颜色以及边框宽度方法
CWinForm窗体控件Panel修改边框颜色以及边框宽度方法1.新建组件这里可以自定义一个Panel控件起名为PanelEx !(https://oscimg.oschina.net/oscnet/99045
Stella981 Stella981
3年前
Flutter 构建完整应用手册
这本食谱包含演示如何在写Flutter应用程序时解决常见问题的食谱。每个配方都是独立的,可以作为参考帮助您构建应用程序。使用主题共享颜色和字体样式为了在整个应用中共享颜色和字体样式,我们可以利用主题。定义主题有两种方式:应用程序范围或使用Theme小部件来定义应用程序特定部分的颜色和字体样式。事实上,应用程序范围
Wesley13 Wesley13
3年前
HTML按钮属性
HTML按钮属性border:none;去掉按钮的边框阴影border:1pxsolideee;按钮边框粗细和颜色backgroundcolor:7ED321;设置按钮背景颜色color:FFFFFF";设置按钮中文字颜色borderradius:15px/50%;圆角按钮text
Wesley13 Wesley13
3年前
CSS背景颜色、背景图片、平铺、定位、固定
CSS背景颜色设置backgroundcolor:red;如设置背景颜色为红色;背景颜色设置支持3种写法:颜色名16进制rgbCSS背景图片颜色设置backgroundimage:url(图片地址);如设置背景图片路径不在说明了!CSS背景图片平铺设置(如果不设置图片默认设置为x轴y轴同时平铺即值为repeat)b
Wesley13 Wesley13
3年前
CSS3 RGBA
CSS3中的rgba中不仅仅让我们像通常一样设置RGB颜色,而且还可以设置其透明度。RGBA像RGB一样设置颜色,而这个”A”——RGBA中的最后一个值——允许我们设置该元素的透明度。就像opacity声明一样,一个opacity值为1的元素是完全不透明的,而一个opacity为0的元素是完全透明的。浏览器兼容性RG
单元格颜色设置
单元格颜色单元格内中经常用到颜色的地方有单元格内的字体颜色和背景颜色,以及边框颜色,当然报表提供一些基本颜色,当这些基本颜色满足不了你的需求时,就可以用到自定义颜色。使用RBG颜色表来调节,并且可以调节色调,饱和度以及亮度。可以通过输入相应的颜色数字来调节
绣鸾 绣鸾
1年前
BetterDisplay Pro for Mac显示器校准软件
是一款由waydabber开发的Mac平台上的显示器校准软件,可以帮助用户调整显示器的颜色和亮度,以获得更加真实、清晰和舒适的视觉体验。以下是BetterDisplayPro的主要特点:显示器校准:可以根据不同的需求和环境条件调整显示器的颜色、亮度和对比度