美文网首页
Flutter主题风格

Flutter主题风格

作者: Imkata | 来源:发表于2023-03-23 16:19 被阅读0次

    在Flutter开发中,我们可以通过定义Theme,复用颜色和字体样式,从而让整个app的设计看起来更一致。

    一. Theme主题的使用

    Theme分为:全局Theme和局部Theme

    主题有两个作用:

    • 设置了主题之后,某些Widget会自动使用主题的样式(比如AppBar的颜色)
    • 将某些样式放到主题中统一管理,在应用程序的其它地方直接引用

    1.1. 全局Theme

    全局Theme会影响整个app的颜色和字体样式。

    使用起来非常简单,只需要向MaterialApp构造器传入ThemeData 即可。

    • 如果没有设置Theme,Flutter将会使用预设的样式。
    • 当然,我们可以对它进行定制。
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        /**
         * 1.一旦设置了主题, 那么应用程序中的某些Widget, 就会直接使用主题的样式
         * 2.可以通过Theme.of(context).textTheme.body2拿到,然后使用
         */
    
        return MaterialApp(
          title: 'Flutter Demo',
          // 全局主题
          theme: ThemeData(
            // 1.亮度 Brightness.dark就是暗黑模式,一般我们不设置
            brightness: Brightness.light,
            // 2.primarySwatch传入不是Color, 而是MaterialColor,是继承于Color的
            // 包含了primaryColor和accentColor
            // 设置完之后,导航条的颜色,TabBar的颜色,Switch的颜色,floatingActionButton的颜色都会改变
            primarySwatch: Colors.red,
            // 3.primaryColor: 单独设置导航和TabBar的颜色,会覆盖primarySwatch
            primaryColor: Colors.orange,
            // 4.accentColor: 单独设置FloatingActionButton、Switch的颜色
            accentColor: Colors.green,
            // 5.Button的主题
            buttonTheme: ButtonThemeData(
              // 默认是88x36
              height: 25,
              minWidth: 10,
              buttonColor: Colors.yellow
            ),
            // 6.Card的主题
            cardTheme: CardTheme(
              // 背景颜色
              color: Colors.greenAccent,
              // 阴影
              elevation: 10
            ),
            // 7.Text的主题
            textTheme: TextTheme(
              // Metarial默认使用的文本,使用的就是body1
              body1: TextStyle(fontSize: 16, color: Colors.red),
              // 就是body1的粗体
              body2: TextStyle(fontSize: 20),
              display1: TextStyle(fontSize: 14),
              display2: TextStyle(fontSize: 16),
              display3: TextStyle(fontSize: 18),
              display4: TextStyle(fontSize: 20),
            )
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("首页")
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text("Hello World"),
                Text("Hello World", style: TextStyle(fontSize: 14),),
                Text("Hello World", style: TextStyle(fontSize: 20),),
                Text("Hello World", style: Theme.of(context).textTheme.body2,),
                Text("Hello World", style: Theme.of(context).textTheme.display3,),
                // 安卓风格的Switch,受primarySwatch的影响
                Switch(value: true, onChanged: (value) {},),
                // iOS风格的Switch,颜色默认是绿色的,不受主题影响,可以通过activeColor修改颜色
                CupertinoSwitch(value: true, onChanged: (value) {}, activeColor: Colors.red,),
                RaisedButton(child: Text("R"), onPressed: () {},),
                Card(child: Text("你好啊,李银河", style: TextStyle(fontSize: 50),),)
              ],
            ),
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                title: Text("首页"),
                icon: Icon(Icons.home)
              ),
              BottomNavigationBarItem(
                  title: Text("分类"),
                  icon: Icon(Icons.category)
              )
            ],
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (ctx) {
                  return HYDetailPage();
                }
              ));
            },
          ),
        );
      }
    }
    

    1.2. 局部Theme

    如果某个具体的Widget不希望直接使用全局的Theme,而希望自己来定义,应该如何做呢?非常简单,只需要在Widget的父节点包裹一下Theme即可。

    创建另外一个新的页面,在新的页面的Scaffold外,包裹了一个Theme,并且设置data为一个新的ThemeData。但是,我们很多时候并不是想完全使用一个新的主题,而是在之前的主题基础之上进行修改:

    class HYDetailPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Theme(
          // 如果传的是个新创建的ThemeData,那么子界面所有的主题都会使用这个新创建的,全局主题就不会生效了
          // 如果我们使用copyWith,就是先拿到全局主题,然后再传入参数覆盖,这样局部主题和全局主题就都还在
          data: Theme.of(context).copyWith(
            primaryColor: Colors.purple
          ),
          child: Scaffold(
            appBar: AppBar(
              title: Text("详情页"),
              backgroundColor: Colors.purple,
            ),
            body: Center(
              child: Text("detail pgae"),
            ),
            // 修改按钮的颜色为pink
            floatingActionButton: Theme(
              data: Theme.of(context).copyWith(
                colorScheme: Theme.of(context).colorScheme.copyWith(
                  secondary: Colors.pink
                )
              ),
              child: FloatingActionButton(
                child: Icon(Icons.pets),
                onPressed: () {
                },
              ),
            ),
          ),
        );
      }
    }
    

    1.3. Flutter中文网错误

    但是这里有一个注意事项:accentColor在这里并不会被覆盖。

    为什么不能覆盖呢?https://github.com/material-components/material-components-flutter-codelabs/issues/106

    我摘抄一点官方人员的回复:

    其实官网文档中之前也出现了类似的错误,比如Flutter中文网之前是翻译官方文档的,https://flutterchina.club/cookbook/design/themes/其中就有该错误。

    后来官网文档中对这个问题进行了修正:

    二. 暗黑Theme适配

    2.1. darkTheme

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

    事实上,MaterialApp中有theme和dartTheme两个参数,dartTheme就是对应暗黑模式下的主题。

    在Flutter中,我们不需要判断用户是否开启了暗黑模式,Flutter会自动帮我们判断,如果用户打开了暗黑模式,会自动使用dartTheme。

    开发中,一般我们单独创建一个文件,用于维护各种模式:

    import 'package:flutter/material.dart';
    
    class HYAppTheme {
      static const double smallFontSize = 16;
      static const double normalFontSize = 22;
      static const double largeFontSize = 24;
    
      static final Color norTextColors = Colors.red;
      static final Color darkTextColors = Colors.green;
    
      // 使用静态属性
      static final ThemeData norTheme = ThemeData(
        primarySwatch: Colors.yellow,
        textTheme: TextTheme(
            body1: TextStyle(fontSize: normalFontSize, color: norTextColors)
        )
      );
    
      static final ThemeData darkTheme = ThemeData(
        primarySwatch: Colors.grey,
        textTheme: TextTheme(
            body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
        )
      );
    }
    

    导入之后直接使用就行:

    import 'package:flutter/material.dart';
    import 'package:learn_flutter/_13_theme/shared/app_theme.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          // 正常主题
          theme: HYAppTheme.norTheme,
          // 暗黑主题
          darkTheme: HYAppTheme.darkTheme,
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("首页"),
          ),
          body: Center(
            child: Text("Hello World"),
          ),
        );
      }
    }
    

    相关文章

      网友评论

          本文标题:Flutter主题风格

          本文链接:https://www.haomeiwen.com/subject/ryowkdtx.html