美文网首页Android
Flutter入门11 -- 主题与屏幕适配

Flutter入门11 -- 主题与屏幕适配

作者: zyc_在路上 | 来源:发表于2022-01-27 16:55 被阅读0次
    • 在Flutter开发中,我们可以通过定义 Theme,复用颜色和字体样式,从而让整个app的设计看起来更一致;
    • Theme分为全局Theme局部Theme
    • 主题的作用:
      • 设置了主题之后,某些Widget会自动使用主题的样式(比如AppBar的颜色);
      • 将某些样式放到主题中统一管理,在应用程序的其它地方直接引用;

    全局Theme

    • 全局Theme会影响整个app的颜色和字体样式;
    • 使用起来非常简单,只需要向MaterialApp构造器传入ThemeData即可;
    • 如果没有设置Theme,Flutter将会使用预设的样式,当然我们可以对它进行定制;

    局部Theme

    • 如果某个具体的Widget不希望直接使用全局的Theme,只需要在Widget的父节点包裹一下Theme即可;
    • 我们很多时候并不是想完全使用一个新的主题,而且在之前的主题基础之上进行修改;
    • 案例代码如下:
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(SFMyApp());
    
    class SFMyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: SFHomePage(),
          theme: ThemeData(
            //亮度 -- 可设置暗黑模式
            brightness: Brightness.light,
            //primarySwatch = primaryColor + accentColor
            primarySwatch: Colors.red,
            //决定导航与tabbar的颜色
            primaryColor: Colors.orange,
            //决定其他组件的颜色
            accentColor: Colors.green,
            //Button的主题
            buttonTheme: ButtonThemeData(
              height: 25,
              minWidth: 50,
              buttonColor: Colors.pink
            ),
            cardTheme: CardTheme(
              elevation: 15,
              color: Colors.purple
            ),
            textTheme: TextTheme(
              bodyText1: TextStyle(fontSize: 16),
              bodyText2: TextStyle(fontSize: 20),
            ),
          ),
        );
      }
    }
    
    class SFHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("基础widget")),
          body: Center(
              child: Column(
                children: [
                  Text("Hello World!!"),
                  Text("Hello World!!",style: Theme.of(context).textTheme.bodyText1,),
                  Text("Hello World!!",style: Theme.of(context).textTheme.bodyText2,),
                  Switch(value: true,onChanged: (value) {},),
                  CupertinoSwitch(value: true,onChanged: (value) {},activeColor: Colors.red,),
                  RaisedButton(child: Text("RaisedButton"),onPressed: () {},),
                  Card(child: Text("liyanyan",style: TextStyle(fontSize: 30),),),
                ],
              )
            ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (ctx) {
                  return SFDetailPage();
                }
              ));
            },
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                title: Text("首页"),
                icon: Icon(Icons.home)
              ),
              BottomNavigationBarItem(
                  title: Text("分类"),
                  icon: Icon(Icons.category)
              )
            ],
          ),
        );
      }
    }
    
    class SFDetailPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Theme(
          data: Theme.of(context).copyWith(
            primaryColor: Colors.purple
          ),
          child: Scaffold(
            appBar: AppBar(
              title: Text("详情页"),
            ),
            body: Center(
              child: Text("详情页"),
            ),
    // 这个地方修改floatingButton背景颜色有点特殊,请注意
            floatingActionButton: Theme(
              data: Theme.of(context).copyWith(
                 colorScheme: Theme.of(context).colorScheme.copyWith(
                   secondary: Colors.pink
                 )
              ),
              child: FloatingActionButton(
                child: Icon(Icons.pets),
                onPressed: () {
    
                },
              ),
            ),
          ),
        );
      }
    }
    
    • primarySwatch:primaryColor + accentColor

    • primaryColor:决定导航与tabbar的颜色

    • accentColor:决定其他组件的颜色

    • buttonTheme:所有按钮的主题

    • cardTheme:所有卡片的主题

    • textTheme:所有文本的主题

    • 新建一个详情页面SFDetailPage,可使用一个新的主题:最外层包裹Theme

    Theme(
          data: ThemeData(
            primaryColor: Colors.purple
          ),
    
    • 也可以在首页主题上进行修改:
    Theme(
          data: Theme.of(context).copyWith(
            primaryColor: Colors.greenAccent
          ),
    

    暗黑Theme适配

    • 主要通过MaterialApp中有themedartTheme两个参数,进行暗黑Theme的适配;
    • 创建一个App主题类SFAppTheme,如下:
    import 'package:flutter/material.dart';
    
    class SFAppTheme {
      
      static const double normalFontSize = 20;
      static const double darkFontSize = 20;
      
      static final Color normalTextColor = Colors.red;
      static final Color darkTextColor = Colors.green;
      
      static final ThemeData normalTheme = ThemeData(
          primarySwatch: Colors.orange,
          textTheme: TextTheme(
              bodyText1: TextStyle(fontSize: normalFontSize,color:normalTextColor)
          )
      );
    
      static final ThemeData darkTheme = ThemeData(
          primarySwatch: Colors.grey,
          textTheme: TextTheme(
              bodyText1: TextStyle(fontSize: darkFontSize,color: darkTextColor)
          )
      );
    }
    
    • 首页代码:
    import 'package:Fluter01/day01/shared/SFAppTheme.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(SFMyApp());
    
    class SFMyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: SFHomePage(),
          theme: SFAppTheme.normalTheme,
          darkTheme: SFAppTheme.darkTheme,
        );
      }
    }
    
    class SFHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("基础widget")),
          body: SFHomeContent()
        );
      }
    }
    
    class SFHomeContent extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text("Hello World!!!"),
        );
      }
    }
    

    Flutter屏幕的适配

    Flutter中的单位
    • 在进行Flutter开发时,我们通常不需要传入尺寸的单位;
    • Flutter使用的是类似于iOS中的点pt,也就是point;
    rpx适配
    • rpx的适配原理,如下所示:
    • 不管是什么屏幕,统一分成750份
    • 在iPhone5上:1rpx = 320/750 = 0.4266 ≈ 0.42px
    • 在iPhone6上:1rpx = 375/750 = 0.5px
    • 在iPhone6plus上:1rpx = 414/750 = 0.552px
    • 那么我们就可以通过上面的计算方式,算出一个rpx,再将自己的size和rpx单位相乘即可:
    • 比如100px的宽度:100 * 2 * rpx
    • 在iPhone5上计算出的结果是84px
    • 在iPhone6上计算出的结果是100px
    • 在iPhone6plus上计算出的结果是110.4px
    • 封装一个屏幕适配工具类SFSizeFit,如下所示:
    import 'dart:ui';
    
    class SFSizeFit {
      static double physicalWidth;
      static double physicalHeight;
      static double screenWidth;
      static double screenHeight;
      static double dpr;
      static double statusHeight;
    
      static double rpx;
      static double px;
    
      static void initialize({double standardSize = 750}) {
        //物理分辨率
        physicalWidth = window.physicalSize.width;
        physicalHeight = window.physicalSize.height;
        print("分辨率: $physicalWidth * $physicalHeight");
    
        //逻辑分辨率
        // final width = MediaQuery.of(context).size.width;
        // final height = MediaQuery.of(context).size.height;
        dpr = window.devicePixelRatio;
    
        screenWidth = physicalWidth / dpr;
        screenHeight = physicalHeight / dpr;
        print("屏幕宽高: $screenWidth * $screenHeight");
    
        //状态栏的高度
        statusHeight = window.padding.top / dpr;
        print("状态栏的高度: $statusHeight");
    
        rpx = screenWidth / standardSize;
        px = screenWidth / standardSize * 2;
      }
    
      static double setRpx(double size) {
        return size * rpx;
      }
    
      static double setPx(double size) {
        return size * px;
      }
    }
    
    • SFSizeFit的使用:
    import 'package:Fluter01/day01/shared/SFSizeFit.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(SFMyApp());
    
    class SFMyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        
        SFSizeFit.initialize();
        
        return MaterialApp(home: SFHomePage());
      }
    }
    
    class SFHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text("基础widget")),
            body: Center(
              child: Container(
                width: SFSizeFit.setPx(200),
                height: SFSizeFit.setPx(200),
                color: Colors.red,
              ),
            )
        );
      }
    }
    
    • 利用扩展Extension对上述代码进行重构
    import 'package:Fluter01/day01/shared/SFSizeFit.dart';
    
    extension SFDoubleFit on double {
      double px() {
        return SFSizeFit.setPx(this);
      }
    
      double rpx() {
        return SFSizeFit.setRpx(this);
      }
    }
    
    import 'package:Fluter01/day01/shared/SFSizeFit.dart';
    
    extension SFIntFit on int {
      double get px {
        return SFSizeFit.setPx(this.toDouble());
      }
    
      double get rpx {
        return SFSizeFit.setRpx(this.toDouble());
      }
    }
    
    • 对double与int进行方法扩展,最后首页调用就变得更加简单了,如下:
    import 'package:Fluter01/day01/shared/SFSizeFit.dart';
    import 'package:flutter/material.dart';
    import 'package:Fluter01/day01/extension/SFIntFit.dart';
    
    void main() => runApp(SFMyApp());
    
    class SFMyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final String message = "Hello World";
        final result = message.sf_split(" ");
    
        print(result);
        return MaterialApp(home: SFHomePage());
      }
    }
    
    class SFHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text("基础widget")),
            body: Center(
              child: Container(
                width: 200.px,
                height: 200.px,
                color: Colors.red,
              ),
            )
        );
      }
    }
    
    extension StringSplit on String {
      List<String> sf_split(String split) {
        return this.split(split);
      }
    }
    

    屏幕适配第三方库

    相关文章

      网友评论

        本文标题:Flutter入门11 -- 主题与屏幕适配

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