zoukankan      html  css  js  c++  java
  • 07-容器类Widget

    容器类Widget

    容器类Widget一般只是包装其子Widget,对其添加一些修饰(补白或背景色等)、变换(旋转或剪裁等)、或限制(大小等)

    Padding

    Padding可以给其子节点添加补白(填充),来看看它的定义:

    Padding({
      ...
      EdgeInsetsGeometry padding,
      Widget child,
    })
    

    EdgeInsetsGeometry是一个抽象类,开发中,我们一般都使用EdgeInsets,它是EdgeInsetsGeometry的一个子类,定义了一些设置补白的便捷方法。

    我们看看EdgeInsets提供的便捷方法:

    • fromLTRB(double left, double top, double right, double bottom):分别指定四个方向的补白。
    • all(double value) : 所有方向均使用相同数值的补白。
    • only({left, top, right ,bottom }):可以设置具体某个方向的补白(可以同时指定多个方向)。
    • symmetric({ vertical, horizontal }):用于设置对称方向的补白,vertical指top和bottom,horizontal指left和right。

    ConstrainedBox和SizedBox

    ConstrainedBox用于对齐子widget添加额外的约束。

    例如 实现一个最小高度为50,宽度尽可能大的红色容器。

    ConstrainedBox(
      constraints: BoxConstraints(
        minWidth: double.infinity, //宽度尽可能大
        minHeight: 50.0 //最小高度为50像素
      ),
      child: Container(
          height: 5.0, 
          child: redBox 
      ),
    )
    

    SizedBox用于给子widget指定固定的宽高,如:

    SizedBox(
       80.0,
      height: 80.0,
      child: redBox
    )
    

    DecoratedBox

    DecoratedBox可以在其子widget绘制前(或后)绘制一个装饰Decoration(如背景、边框、渐变等)。DecoratedBox定义如下:

    const DecoratedBox({
      Decoration decoration,
      DecorationPosition position = DecorationPosition.background,
      Widget child
    })
    
    • decoration:代表将要绘制的装饰,它类型为Decoration,Decoration是一个抽象类,它定义了一个接口 createBoxPainter(),子类的主要职责是需要通过实现它来创建一个画笔,该画笔用于绘制装饰。
    • position:此属性决定在哪里绘制Decoration,它接收 DecorationPosition的枚举类型,该枚举类两个值:background:在子widget之后绘制,即背景装饰。foreground:在子widget之上绘制,即前景。

    decoration通常会直接使用BoxDecoration,它是一个Decoration的子类,实现了常用的装饰元素的绘制。

    BoxDecoration({
      Color color, //颜色
      DecorationImage image,//图片
      BoxBorder border, //边框
      BorderRadiusGeometry borderRadius, //圆角
      List<BoxShadow> boxShadow, //阴影,可以指定多个
      Gradient gradient, //渐变
      BlendMode backgroundBlendMode, //背景混合模式
      BoxShape shape = BoxShape.rectangle, //形状
    })
    

    Transform变换

    Transform可以在其子Widget绘制时对其应用一个矩阵变换(transformation),Matrix4是一个4D矩阵,通过它我们可以实现各种矩阵操作。下面是一个例子:

    Container(
      color: Colors.black,
      child: new Transform(
        alignment: Alignment.topRight, //相对于坐标系原点的对齐方式
        transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度
        child: new Container(
          padding: const EdgeInsets.all(8.0),
          color: Colors.deepOrange,
          child: const Text('Apartment for rent!'),
        ),
      ),
    );
    

    Container

    Container是DecoratedBox、ConstrainedBox、Transform、Padding、Align等widget的一个组合widget。所以我们只需通过一个Container可以实现同时需要装饰、变换、限制的场景。下面是Container的定义:

    Container({
      this.alignment,
      this.padding, //容器内补白,属于decoration的装饰范围
      Color color, // 背景色
      Decoration decoration, // 背景装饰
      Decoration foregroundDecoration, //前景装饰
      double width,//容器的宽度
      double height, //容器的高度
      BoxConstraints constraints, //容器大小的限制条件
      this.margin,//容器外补白,不属于decoration的装饰范围
      this.transform, //变换
      this.child,
    })
    

    示例:

    Container(
      margin: EdgeInsets.only(top: 50.0, left: 120.0), //容器外补白
      constraints: BoxConstraints.tightFor( 200.0, height: 150.0), //卡片大小
      decoration: BoxDecoration(//背景装饰
          gradient: RadialGradient( //背景径向渐变
              colors: [Colors.red, Colors.orange],
              center: Alignment.topLeft,
              radius: .98
          ),
          boxShadow: [ //卡片阴影
            BoxShadow(
                color: Colors.black54,
                offset: Offset(2.0, 2.0),
                blurRadius: 4.0
            )
          ]
      ),
      transform: Matrix4.rotationZ(.2), //卡片倾斜变换
      alignment: Alignment.center, //卡片内文字居中
      child: Text( //卡片文字
        "5.20", style: TextStyle(color: Colors.white, fontSize: 40.0),
      ),
    )
    

    可以看到Container通过组合多种widget来实现复杂强大的功能,在Flutter中,这也正是组合优先于继承的实例。

    Scaffold、TabBar、底部导航

    Material库提供了很多Widget,本节介绍一些常用的Widget,其余的读者可以查看文档。

    大多数路由页都会包含一个导航栏,有些路由页可能会有抽屉菜单(Drawer)以及底部Tab导航菜单等。Flutter Material库提供了一个Scaffold Widget,它是一个路由页的骨架,可以非常容易的拼装出一个完整的页面。

    我们实现一个页面,它包含:

    一个导航栏
    导航栏右边有一个分享按钮
    有一个抽屉菜单
    有一个底部导航
    右下角有一个悬浮的动作按钮
    

    实现代码如下:

    class ScaffoldRoute extends StatefulWidget {
      @override
      _ScaffoldRouteState createState() => _ScaffoldRouteState();
    }
    
    class _ScaffoldRouteState extends State<ScaffoldRoute> {
      int _selectedIndex = 1;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar( //导航栏
            title: Text("App Name"), 
            actions: <Widget>[ //导航栏右侧菜单
              IconButton(icon: Icon(Icons.share), onPressed: () {}),
            ],
          ),
          drawer: new MyDrawer(), //抽屉
          bottomNavigationBar: BottomNavigationBar( // 底部导航
            items: <BottomNavigationBarItem>[
              BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
              BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
              BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
            ],
            currentIndex: _selectedIndex,
            fixedColor: Colors.blue,
            onTap: _onItemTapped,
          ),
          floatingActionButton: FloatingActionButton( //悬浮按钮
              child: Icon(Icons.add),
              onPressed:_onAdd
          ),
        );
      }
      void _onItemTapped(int index) {
        setState(() {
          _selectedIndex = index;
        });
      }
      void _onAdd(){
      }
    }
    

    上面代码中我们用到了另外几个Widget:

    AppBar

    AppBar是一个Material风格的导航栏,它可以设置标题、导航栏菜单、底部Tab等。下面我们看看AppBar的定义:

    AppBar({
      Key key,
      this.leading, //导航栏最左侧Widget,常见为抽屉菜单按钮或返回按钮。
      this.automaticallyImplyLeading = true, //如果leading为null,是否自动实现默认的leading按钮
      this.title,// 页面标题
      this.actions, // 导航栏右侧菜单
      this.bottom, // 导航栏底部菜单,通常为Tab按钮组
      this.elevation = 4.0, // 导航栏阴影
      this.centerTitle, //标题是否居中 
      this.backgroundColor,
      ...   //其它属性见源码注释
    })
    

    TabBar

    通过“bottom”属性来添加一个导航栏底部tab按钮组,Material组件库中提供了一个TabBar组件,它可以快速生成Tab菜单,示例:

    class _ScaffoldRouteState extends State<ScaffoldRoute>
        with SingleTickerProviderStateMixin {
    
      TabController _tabController; //需要定义一个Controller
      List tabs = ["新闻", "历史", "图片"];
    
      @override
      void initState() {
        super.initState();
        // 创建Controller  
        _tabController = TabController(length: tabs.length, vsync: this);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            ...   //省略无关代码
            bottom: TabBar(   //生成Tab菜单
                controller: _tabController,
                tabs: tabs.map((e) => Tab(text: e)).toList()),
            ),
          ... //省略无关代码
    
      }
    

    上面代码首先创建了一个TabController ,它是用于控制/监听Tab菜单切换。然后通过TabBar生成了一个底部菜单栏,TabBar的tabs属性接受一个Widget数组,表示每一个Tab子菜单,我们可以自定义,也可以像示例中一样直接使用Tab Widget,它也是Material组件库提供的Material风格的Tab菜单。

    Tab Widget有三个可选参数,除了可以指定文字外,还可以指定Tab菜单图标,或者直接自定义Widget,定义如下:

    Tab({
      Key key,
      this.text, // 菜单文本
      this.icon, // 菜单图标
      this.child, // 自定义Widget
    })
    

    TabBarView

    通过TabBar我们只能生成一个静态的菜单,如果要实现Tab页,Material库提供了一个TabBarView组件,它可以很轻松的配合TabBar来实现同步切换和滑动状态同步,示例如下:

    Scaffold(
      appBar: AppBar(
        ... //省略无关代码
        bottom: TabBar(
          controller: _tabController,
          tabs: tabs.map((e) => Tab(text: e)).toList()),
      ),
      drawer: new MyDrawer(),
      body: TabBarView(
        controller: _tabController,
        children: tabs.map((e) { //创建3个Tab页
          return Container(
            alignment: Alignment.center,
            child: Text(e, textScaleFactor: 5),
          );
        }).toList(),
      ),
      ... // 省略无关代码  
    )
    

    TabBar和TabBarView的controller是同一个!正是如此,TabBar和TabBarView正是通过同一个controller来实现菜单切换和滑动状态同步的。

    Drawer

    Scaffold的drawer和endDrawer属性可以分别接受一个Widget作为页面的左、右抽屉菜单,当用户手指重屏幕左/右向里滑动时便可打开抽屉菜单。示例如下:

    class MyDrawer extends StatelessWidget {
      const MyDrawer({
        Key key,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Drawer(
          child: MediaQuery.removePadding(
            context: context,
            // DrawerHeader consumes top MediaQuery padding.
            removeTop: true,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(top: 38.0),
                  child: Row(
                    children: <Widget>[
                      Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 16.0),
                        child: ClipOval(
                          child: Image.asset(
                            "imgs/avatar.png",
                             80,
                          ),
                        ),
                      ),
                      Text(
                        "Wendux",
                        style: TextStyle(fontWeight: FontWeight.bold),
                      )
                    ],
                  ),
                ),
                Expanded(
                  child: ListView(
                    children: <Widget>[
                      ListTile(
                        leading: const Icon(Icons.add),
                        title: const Text('Add account'),
                      ),
                      ListTile(
                        leading: const Icon(Icons.settings),
                        title: const Text('Manage accounts'),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    FloatingActionButton

    FloatingActionButton是Material设计规范中的一种特殊Button,通常悬浮在页面的某一个位置作为某种常用动作的快捷入口

    底部Tab导航栏

    可以通过Scaffold的bottomNavigationBar属性来设置底部导航,Material组件库提供的BottomNavigationBar和BottomNavigationBarItem两个Widget来实现Material风格的底部导航栏。

    Material组件库中提供了一个BottomAppBar Widget,可以和FloatingActionButton配合实现"打洞"效果。源码如下:

    bottomNavigationBar: BottomAppBar(
      color: Colors.white,
      shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
      child: Row(
        children: [
          IconButton(icon: Icon(Icons.home)),
          SizedBox(), //中间位置空出
          IconButton(icon: Icon(Icons.business)),
        ],
        mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
      ),
    )
    

    可以看到,上面代码中没有控制打洞位置的属性,实际上,打洞的位置取决于FloatingActionButton的位置,上面FloatingActionButton的位置为:

    floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    

    BottomAppBar的shape属性决定洞的外形,CircularNotchedRectangle实现了一个圆形的外形。

  • 相关阅读:
    【小白成长撸】--二分查找
    【ACM小白成长撸】--贪婪法解硬币找零问题
    【小白成长撸】--Fibonacci
    【ACM小白成长撸】--计算单词个数
    【小白成长撸】--多项式求圆周率PI
    【小白成长撸】--循环顺序队列
    【小白成长撸】--链栈(C语言版)
    盘前预测-3.15
    盘前预测-3.11
    盘前预测-3.10
  • 原文地址:https://www.cnblogs.com/SLchuck/p/10326710.html
Copyright © 2011-2022 走看看