Drat与Flutter基础

作者: 十年之后_b94a | 来源:发表于2020-06-21 08:04 被阅读0次

    Drat语法

    1、基本语法

    返回修饰词 main (){}

    void main(){
    //程序的主入口
    }
    

    2、基本数据类型

    Drat是强类型语言
    var 代表不确定类型 当变量第一次被赋值时,那么该变量就是当前类型,重新赋值不能改变类型
    int 整数型
    double 浮点型
    bool 布尔型
    String 字符串类型
    num数字类型不区分浮点与整数型

    void main(){
      int a = 10;
      a = 10.1;//会报错
      String b = "this is Drat";
      bool flag = true;
      double c = 10.2; 
      var d = 123;
      d = "123";//报错
    }
    

    3、final 与const

    final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const要求在声明时初始化,并且赋值必需为编译时常量。

    void main(){
    }
    
    

    List集合

    List类似前端的数组,注意的是
    add()增加元素

    List a = new List();
    a.add(456)
    

    class Person{
      String name;
      int age;
      Person(String name,int age){
        this.name = name;
        this.age = age;
      }//构造函数一种写法
      Person(this.name,this.age){}//构造函数一种写法
      
      void printInfo(){
        print("${this.name} --- ${this.age}");
      }
    }
    

    Flutter

    1、区分StatefullWidgetStatelessWidget的区别

    StatefullWidget:为动态组件,状态会发生改变的,例如进度条等。
    StatelessWidget:静态组件,例如文本框。

    2、Hello Word

    import 'package:flutter/material.dart';
    
    void main()=>runApp(MyApp());
    
    class MyApp extends StatelessWidget{
      @override
      Widget build(BuildContext context){
        return MaterialApp(
          title : "欢迎学习,Flutter",
          home : Scaffold(
            appBar : AppBar(title:Text("Hello Wrod")),
            body : Center(child:Text("Hello Wrod",style:TextStyle(color:Colors.blue,fontSize:50)))
          )
        );
      }
    }
    
    image.png

    3、MaterialApp组件

    MaterialApp组件一般是我们的根组件他可以定义我们的APP的主题颜色,已经背景等
    查看MaterialApp源文件

    MaterialApp({
      Key key,
      this.title = '', // 设备用于为用户识别应用程序的单行描述
      this.home, // 应用程序默认路由的小部件,用来定义当前应用打开的时候,所显示的界面
      this.color, // 在操作系统界面中应用程序使用的主色。
      this.theme, // 应用程序小部件使用的颜色。
      this.routes = const <String, WidgetBuilder>{}, // 应用程序的顶级路由表
      this.navigatorKey, // 在构建导航器时使用的键。
      this.initialRoute, // 如果构建了导航器,则显示的第一个路由的名称
      this.onGenerateRoute, // 应用程序导航到指定路由时使用的路由生成器回调
      this.onUnknownRoute, // 当 onGenerateRoute 无法生成路由(initialRoute除外)时调用
      this.navigatorObservers = const <NavigatorObserver>[], // 为该应用程序创建的导航器的观察者列表
      this.builder, // 用于在导航器上面插入小部件,但在由WidgetsApp小部件创建的其他小部件下面插入小部件,或用于完全替换导航器
      this.onGenerateTitle, // 如果非空,则调用此回调函数来生成应用程序的标题字符串,否则使用标题。
      this.locale, // 此应用程序本地化小部件的初始区域设置基于此值。
      this.localizationsDelegates, // 这个应用程序本地化小部件的委托。
      this.localeListResolutionCallback, // 这个回调负责在应用程序启动时以及用户更改设备的区域设置时选择应用程序的区域设置。
      this.localeResolutionCallback, // 
      this.supportedLocales = const <Locale>[Locale('en', 'US')], // 此应用程序已本地化的地区列表 
      this.debugShowMaterialGrid = false, // 打开绘制基线网格材质应用程序的网格纸覆盖
      this.showPerformanceOverlay = false, // 打开性能叠加
      this.checkerboardRasterCacheImages = false, // 打开栅格缓存图像的棋盘格
      this.checkerboardOffscreenLayers = false, // 打开渲染到屏幕外位图的图层的棋盘格
      this.showSemanticsDebugger = false, // 打开显示框架报告的可访问性信息的覆盖
      this.debugShowCheckedModeBanner = true, // 在选中模式下打开一个小的“DEBUG”横幅,表示应用程序处于选中模式
    }) 
    

    利用MaterialApp修改我们App的主题

        return MaterialApp(title: "MaterialApp",
          home:Scaffold(
            appBar : AppBar(title:Text("Flutter MaterialApp"))
          ),
          theme:ThemeData(
            primaryColor: Colors.yellow,//appbar为黄色
            scaffoldBackgroundColor:Colors.pink//设置底层背景为粉红色
          )
        );
    
    image.png

    4、Container组件的属性

    Container类似前端的div元素

    属性 描述 用法
    child Container容器中包含的子组件 child: Text("这是Container的子组件")
    alignment 将子组件对齐方式 alignment:Alignment.bottomRight
    width Container容器的宽度 width:300.0
    height Container容器的高度 height:300.0
    padding Container容器的内边距 padding: EdgeInsets.all(20.0) 或者 padding: EddgeInsets.fromLTRB(double left, double top, double right, double bottom)
    margin Container容器的外边距 margin: EdgeInsets.all(20.0) 或者 margin: EddgeInsets.fromLTRB(double left, double top, double right, double bottom)
    color Container容器的背景色 Colors.yellow 或者 color:Color.fromRGBO(r, g, b, opacity)
    decoration 装饰器,可以设置边框、圆角、背景图片,背景色:注意此属性不能与color属性同时使用 具体可看代码
    transform 变换旋转 transform: Matrix4.rotationZ(0.3)
    @override
      Widget build(BuildContext context) {
        return Container(
          child:Text("这是包裹在Container组件内的Text组件。"),
          alignment : Alignment.bottomRight,
          width: 300.0,
          height: 300.0,
          padding: EdgeInsets.fromLTRB(20.0, 40.0, 60.0, 40.0),
          margin: EdgeInsets.all(40.0),
          //color: Colors.yellow,//背景色不能与decoration同时使用,
          decoration:BoxDecoration(
            color: Colors.yellow,//背景色
            border: Border.all(//线条样式
              color: Colors.black,//边框颜色
              width: 3.0,//线框
              style: BorderStyle.solid,//实现
            ),
            image: DecorationImage(//背景图片 注意使用https地址图片
              image: NetworkImage('https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2606284972,2670724825&fm=26&gp=0.jpg'),
              alignment: Alignment.topCenter,//对齐方式
            ),
          )
        );
      }
    
    image.png

    4-1使用Container制造圆形且带阴影

    Container(
      width:50.0,
      height:50.0,
      decoration:BoxDecoration(
        color:Colors.yellow,//背景图片
        shape:BoxShape.circle,//圆形
        boxShadow:[BoxShadow(color: Colors.black, offset: Offset(0, 0),blurRadius: 2.0, spreadRadius: 0.0)]//阴影
      )
    )
    

    5、TextWidget常用属性

    属性 描述 用法
    textAlign 文本对齐方式 textAlign:TextAlign.right
    overflow 文本超出样式 overflow:TextOverflow.ellipsis,
    maxLines 最大显示的行数 maxLines:2
    style 设置文本样式 style:TextStyle(color:Color.fromRGBO(25, 201, 124, 1),)
    style_color 属于style属性下,设置文本颜色 style:TextStyle(color:Color.fromRGBO(25, 201, 124, 1),)
    style_fontSize 属于style属性下,设置文本字体大小 style:TextStyle(fontSize: 20.0,)
    style_fontWeight 属于style属性下,设置文本字体加粗 style:TextStyle(fontWeight: FontWeight.w800,)
    style_fontStyle 属于style属性下,设置文本字体斜体 style:TextStyle(fontStyle: FontStyle.italic,)
    style_decoration 属于style属性下,设置文本中间的横线 style: TextStyle(decoration: TextDecoration.lineThrough)
    Text("这是包裹在Container组件中的Text子组件",
    style:TextStyle(
      color:Color.fromRGBO(30, 201, 124, 1),
      fontSize: 20.0,
      fontWeight: FontWeight.w800,
      fontStyle: FontStyle.italic
      ),
     textAlign:TextAlign.right,
     overflow:TextOverflow.ellipsis,
     maxLines:2
    )
    

    6、Image组件

    Image一种是网络图片、一种是本地图片
    Image.network()Image.asset()

    属性 描述 用法
    默认值 图片应用的地址 -
    color 图片的颜色,直接使用会覆盖掉图片 color:Colors.pink
    colorBlendMode 混合模式,类似在图片上弄个遮罩需要与color一起使用 colorBlendMode: BlendMode.screen
    fit 图片裁剪 fit:BoxFit.cover
    repeat 平铺 repeat: ImageRepeat.repeat,

    BoxFit.cover:可能拉伸,裁切,充满,但是不会是图片变形
    BoxFit.fill:图片充满整个容器,会被拉伸
    BoxFit.contain:全图显示(不是充满容器),保留原宽高比例,会有空隙

    BoxFit.cover

    image.png
    BoxFit.fill
    image.png
    BoxFit.contain
    image.png
        Container(
            width:300.0,
            height:300.0,
            child: Image.network(//设置远程图片
              "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2606284972,2670724825&fm=26&gp=0.jpg",
              fit: BoxFit.cover,
              repeat: ImageRepeat.repeat,
              color:Colors.green,
              colorBlendMode: BlendMode.screen,
            ),
            decoration: BoxDecoration(
              //color:Colors.lime
              border: Border.all(
                color:pink,
                width: 4,
                style: BorderStyle.solid
              ),
            ),
          )
    
    image.png

    6-1、 实现圆角、以及实现圆形图片

    第一种:使用container背景图片实现圆形图片

        Container(
            width: 300.0,
            height: 300.0,
            decoration: BoxDecoration(
              image: DecorationImage(
                image: NetworkImage("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2606284972,2670724825&fm=26&gp=0.jpg"),
                fit:BoxFit.cover//填充背景图片
              ),
              border:Border.all(
                width:3,
                color:Colors.black,
                style: BorderStyle.solid
              ),
              borderRadius:BorderRadius.circular(150.0)//圆角
            ),
          )
    

    第二种利用ClipOval组件实现圆形图片 推荐使用这个

    return Center(
      child:Container(
        child:ClipOval(
          child:Image.network("图片地址",width:100.0,height:100.0,fit:BoxFit.cover)
        )
      )
    );
    

    第三种利用CircleAvatar

    CircleAvatar(
          radius: 25.0,
          backgroundImage: NetworkImage("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2611652800,2596506430&fm=26&gp=0.jpg")
        );
    

    6-3、image组件之引入本地图片

    1、在项目根目录创建静态资源文件夹

    创建images文件夹其次在这个文件夹下创建2.0x和3.0x文件夹,然后把静态资源考进去,images文件夹下也得拷一份

    - images
    -- 2.0x
    -- 3.0x
    
    image.png

    2、在配置文件夹中写入刚刚的路径

    pubspec.yaml注意:格式需要对齐,不要保留空格

      assets:
        - images/1.jpg
        - images/3.0x/1.jpg
        - images/2.0x/1.jpg
    

    3、使用图片

          Container(
            child: ClipOval(
              child: Image.asset("images/1.jpg",width: 200.0,height: 200.0,fit: BoxFit.fill,),
            ),
          )
    
    image.png

    7、 Icon组件

    图标组件

    属性 描述 用法
    size 图标大小 size: 30.0,
    color 图标颜色 color: Colors.blue,
    基础值 具体的图标 Icons.sentiment_neutral
        Icon(Icons.sentiment_neutral,
           size: 30.0,
            color: Colors.blue
         )
    
    image.png

    8、 ListView组件

    ListView:基础列表组件、水平列表组件、图标组件,相当于父容器,里面很多子元素

    属性 描述 使用
    scrollDirection 定义水平列表还是垂直列表 scrollDirection:Axis.horizontal水平列表scrollDirection:Axis.vertical垂直列表
    padding 外边距 padding: EdgeInsets.all(20.0) 或者 padding: EddgeInsets.fromLTRB(double left, double top, double right, double bottom)
    children 子元素,并且它的子元素不是一个,数组类型 children:[Container(width: 100.0,height: 100.0,color: Colors.red,),Container(width: 100.0,height: 100.0,color: Colors.blue,),Container(width: 100.0,height: 100.0,color: Colors.pink,),Container(width: 100.0,height: 100.0,color: Colors.yellow,)]
        ListView(
            scrollDirection:Axis.vertical,
            children: <Widget>[
              Image.asset("images/1.jpg",width: 200.0,height: 300.0,fit: BoxFit.fill,),
              Text("这是列表组件"),
              Container(width: 200.0,height: 200.0,color: Colors.red,)
            ],
          ),
    

    8-1、我们可以配合ListTitle一起使用

    ListTitle组件,类似于新闻页主标题,副标题

    属性 描述 使用
    leading 前面的图标 leading:ICon(Icons.sentiment_neutral)也可以图片
    trailing 后面的图标 trailing:ICon(Icons.sentiment_neutral)
    title 一级标题 title:Text("阿斯达四大")
    title 一级标题 title:Text("阿斯达四大")
    subtitle 二级标题/文本/描述 subtitle:Text("阿斯达四大")
    image.png

    这是listTtile的效果

    ListView(
            scrollDirection:Axis.vertical,
            padding: EdgeInsets.all(10.0),
            children: <Widget>[
              Container(
                margin: EdgeInsets.fromLTRB(0, 0, 0, 10.0),
                child: ListTile(
                  leading:Image.asset("images/1.jpg",width: 100.0,height: 200.0,fit: BoxFit.fill,),
                  trailing:Icon(Icons.sentiment_neutral,
                    size: 30.0,
                    color: Colors.blue
                  ),
                  title:Container(
                    child:Text(
                      "黄山旅游董事长章德辉:景区停摆每天损失450万 关注“两只票”",
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.w900
                      ),
                    ),
                  ),
                  subtitle: Text("1月13日-23日,每日经济新闻推出《专访董事长·第一季》,华谊兄弟董事长王中军、工业富联董事长李军旗、通威集团董事局主席刘汉元等多位重磅嘉宾接受采访,畅谈行业现状、直陈公司利弊、展望未来前景,在业界引发强烈反响。时值全国“两会”即将召开之际,又是抗击新冠肺炎疫情之后,行业、企业复苏的关键时刻,作为知名上市公司的领头人,他们怎样看待疫情对行业、企业的影响?又如何带领企业走出困境?")
                ),
              ),
              Container(
                margin: EdgeInsets.fromLTRB(0, 0, 0, 10.0),
                child: ListTile(
                  leading:Image.asset("images/1.jpg",width: 100.0,height: 200.0,fit: BoxFit.fill,),
                  trailing:Icon(Icons.sentiment_neutral,
                    size: 30.0,
                    color: Colors.blue
                  ),
                  title:Container(
                    child:Text(
                      "黄山旅游董事长章德辉:景区停摆每天损失450万 关注“两只票”",
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.w900
                      ),
                    ),
                  ),
                  subtitle: Text("1月13日-23日,每日经济新闻推出《专访董事长·第一季》,华谊兄弟董事长王中军、工业富联董事长李军旗、通威集团董事局主席刘汉元等多位重磅嘉宾接受采访,畅谈行业现状、直陈公司利弊、展望未来前景,在业界引发强烈反响。时值全国“两会”即将召开之际,又是抗击新冠肺炎疫情之后,行业、企业复苏的关键时刻,作为知名上市公司的领头人,他们怎样看待疫情对行业、企业的影响?又如何带领企业走出困境?")
                ),
              )
            ],
          ),
    
    image.png

    9、动态组件

    动态组件:顾名思义会改变组件状态的,例如进度条,或者我们拿到数据循环出组件(类似v-for);

    我们看个简单的例子

    我们要知道 任何的组件他都是一个类,只不过继承了StatelessWidget、StatefulWidget这两个接口,只要是类,他就有变量成员,方法

    class MyBody extends StatelessWidget{
    
      List<Widget> _getData(){ //重点
    
        List<Widget> MyList = new List();
    
        for(int i = 0 ;i<20;i++){
          MyList.add(
            Container(
              padding: EdgeInsets.all(10.0),
              child: ListTile(
                leading: Icon(Icons.smartphone,size: 38,color: Colors.pink,),
                title: Text("美药企跳过疫苗研发关键实验环节 美股还会暴跌吗",style: TextStyle(fontSize: 22,fontWeight: FontWeight.w900),),
                subtitle: Text("据报道,美药企跳过疫苗研发关键实验环节,直接进行人体临床试验,而该做法备受一些专家质疑。美国总统特朗普此前称,争取2020年年底前实现疫苗的量产和分发。值得一提的是,对于加速疫苗的研发,一些美国网友也不认同上述做法。",maxLines:3,overflow:TextOverflow.ellipsis),
              ),
            )
          );
        }
        return MyList;
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return ListView(
          children: this._getData()
        );
      }
    }
    
    image.png

    mock拿取服务端数据渲染

    //服务端数据
    var ListData = [
      {
        "title":"北京一单位33人集中发热 初判与使用中央空调有关",
        "pubtime":2,
        "reply":255,
        "imageUrl" : "https://cms-bucket.ws.126.net/2020/0518/f55c820fp00qaitd8006qc000s600e3c.png"
      },
      {
        "title":'蓬佩奥"警告"中国不要干涉美记者在港工作 中方回应',
        "pubtime":4,
        "reply":0,
        "imageUrl" : "https://cms-bucket.ws.126.net/2020/0518/b76b3832j00qaiq2u004tc000s600e3c.jpg"
      },
      {
        "title":"失联6天的翼装女飞行员遇难:遗体最先被村民发现",
        "pubtime":5,
        "reply":"4.2万",
        "imageUrl" : "https://cms-bucket.ws.126.net/2020/0518/4689114ep00qail4e00e9c000s600e3c.png"
      }]
    
     List<Widget> _getData(){
        var result = ListData.map((item){
          return ListTile(
            leading: Image.network(item["imageUrl"],width: 90.0,height:60.0,fit: BoxFit.fill,),
            title:Text(item["title"],style: TextStyle(fontSize:18,fontWeight: FontWeight.w900),),
            subtitle: Row(
              children: <Widget>[
                Container(child: Text(item['pubtime'].toString() + "小时前"),margin: EdgeInsets.fromLTRB(0, 10, 10.0, 0),),
                Container(child: Text(item['reply'].toString() + "万人查看"),margin: EdgeInsets.fromLTRB(0, 10, 10.0, 0),)
              ],
            )
          );
        });
    
        return result.toList();
      }
    
    image.png

    9-1、ListView.builder方法

    itemCount必填参数:循环的次数
    itemBuilder:循环主体

    @override
      Widget build(BuildContext context) {
        return ListView.builder(//两个必填参数itemCount循环次数
          itemCount: ListData.length,
          itemBuilder: (ctx,index){//itemBuilder循环体 
            return ListTile(
              leading: Image.network(ListData[index]["imageUrl"],width: 90.0,height: 70.0,fit: BoxFit.fill,),
              title: Text(ListData[index]["title"],style: TextStyle(fontSize: 20,fontWeight: FontWeight.w900),),
            );
          }
        );
    

    10、GridView组件

    image.png

    栅格布局:
    常用有两种方法:
    1、GridView.builder()
    2、GridView.count()
    请注意当GridView被放在Cloumn或者Row里面使用那么一定要指定GridView的高度,即在GridView外面包裹一层Container并设置高度

    属性 描述 用法
    scrollDirection 横向还是纵向 scrollDirection:Axis.vertical
    padding 内边距 同上面几个例子
    resolve 组件反向排序 resolve:true
    crossAxisSpacing 一行子元素的间距 crossAxisSpacing:20.0
    mainAxisSpacing 垂直方向的子元素的间距 mainAxisSpacing:20.0
    crossAxisCount 一行子元素的个数 crossAxisCount:3
    childAspectRatio 子元素的宽高比例 childAspectRatio:1.0
    chidren 子元素集合 chidren:<Widget>[]
    gridDelegate 该属性是GridView.builder中使用 gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3,mainAxisSpacing : 20.0,crossAxisSpacing :20.0,childAspectRatio:0.6)
    physics 禁止滚动 physics:NeverScrollableScrollPhysics()

    10-1、简单的例子

    GridView.count

    List<Widget> _getData(){
       var list = ListData.map((i){
          return Container(
              decoration: BoxDecoration(
                border: Border.all(width:0.3,style: BorderStyle.solid,color: Colors.pink)
              ),
              child: Column(
                children: <Widget>[
                  Image.network(i["imageUrl"],height: 50.0,fit: BoxFit.cover,),
                  Container(height: 10.0,),
                  Text(i["title"],style: TextStyle(fontSize: 13.0),maxLines: 2,overflow: TextOverflow.ellipsis)
                ],
              ),
            );
        });
        return list.toList();
      }
      
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return GridView.count(
          crossAxisCount: 3,//一行三个
          crossAxisSpacing:20.0,//一行之间的间距
          mainAxisSpacing:20.0,//垂直之间的间距
          padding: EdgeInsets.all(10.0),//内边距
          children:this._getData(),//子元素
          childAspectRatio:0.6,//设置宽高比例不能直接设置宽高的
        );
      }
    

    10-2、GridView.builder

    Widget _getData(ctx,i){
          return Container(
            decoration: BoxDecoration(
              border: Border.all(width:0.3,style: BorderStyle.solid,color: Colors.pink)
            ),
            padding: EdgeInsets.fromLTRB(15, 5, 15, 10),
            child: Column(
              children: <Widget>[
                Image.network(ListData[i]["imageUrl"],height: 120.0,fit: BoxFit.fitWidth,),
                SizedBox(height: 6.0,),//撑开间距
                Text(ListData[i]["title"],style: TextStyle(fontSize: 16.0,fontWeight: FontWeight.w900),maxLines: 2,overflow: TextOverflow.ellipsis)
              ],
            ),
          );
      }
      
      @override
      Widget build(BuildContext context) {
        return GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount:3,
            mainAxisSpacing : 20.0,
            crossAxisSpacing :20.0,
            childAspectRatio:0.6
          ),
          padding: EdgeInsets.all(10.0),
          itemCount: ListData.length,
          itemBuilder: this._getData//注意这个地方不是调用哦
        );
      }
    

    11、Padding、Row、Colum、Expanded组件

    11-1、Padding组件

    在Flutter中有许多组件是没有padding属性,我们之前都是包裹在Container中使用

    属性 描述 使用
    padding 内边距 padding:EdgeInsets.all(10.0)
    child 子元素 child:Text()
    ListView(
      children:[
        Padding(padding:padding:EdgeInsets.all(10.0),child:Text("121313")),
        Padding(padding:padding:EdgeInsets.all(10.0),child:Text("121313")),
      ]
    )
    

    11-2、Row组件

    水平列表组件
    mainAxisAlignment、crossAxisAlignment是相对于父元素的位置起值的,如果父元素没有那么默认就是内容的高度

    属性 描述 使用
    crossAxisAlignment 纵轴对齐方式 crossAxisAlignment:CrossAxisAlignment.spaceEvenly
    mainAxisAlignment 横轴对齐方式 mainAxisAlignment:MainAxisAlignment.center
    children 子元素集合 children:[]

    测试下mainAxisAlignment、crossAxisAlignment

         return Container(
          width: 800.0,
          height: 600.0,
          color: Colors.lightBlue,
          child: Row(
            children: <Widget>[
              Container(width: 100.0,height: 100.0,color: Colors.red,),
              Container(width: 100.0,height: 100.0,color: Colors.orange,),
              Container(width: 100.0,height: 100.0,color: Colors.green,)
            ],
          )
        );
    

    默认情况

    默认情况
    MainAxisAlignment.spaceEvenly均匀分配横轴方向
    image.png
    纵轴相对于父元素,如果父元素没有,那么无效CrossAxisAlignment.start image.png

    11-2、Colum组件

    用法属性和Row一致

    mainAxisAlignment:MainAxisAlignment.spaceEvenly,
    crossAxisAlignment:CrossAxisAlignment.start,
    
    image.png

    11-3、Expanded组件

    Expanded组件类似于前端的display:flex;
    注意一点:在Row中使用Expanded的时候,无法指定Expanded中的子组件的宽度width,但可以指定其高度height。同理,在Column中使用Expanded的时候,无法指定Expanded中的子组件的高度height,可以指定宽度width。

    属性 描述 使用
    flex 占用多少格 flex:1整数型
    child 子元素 child:Text()

    和Row组件一起使用

    @override
      Widget build(BuildContext context) {
        return Row(
            children: <Widget>[
              Expanded(child: Container(height:200.0,color: Colors.blue,),flex: 1),
              Expanded(child: Container(height:200.0,color: Colors.pink),flex: 2),
            ],
          );
      }
    
    image.png

    和Cloumn一起使用

    return Column(
            children: <Widget>[
              Expanded(child: Container(height:200.0,color: Colors.blue,),flex: 1),
              Expanded(child: Container(height:200.0,color: Colors.pink),flex: 2),
            ],
          );
    
    image.png

    12、布局小结

    实现下面的布局


    image.png
    @override
      Widget build(BuildContext context) {
        return Padding(padding: EdgeInsets.all(10.0),
          child: Column(
            children: <Widget>[
              Container(height: 180.0,color: Colors.black,),
              Padding(
                padding: EdgeInsets.fromLTRB(0, 10.0, 0, 0),
                child: Row(
                  children: <Widget>[
                    Expanded(
                      child: Padding(padding: EdgeInsets.fromLTRB(0, 0, 10.0, 0),child: Image.network("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1809168321,2277139267&fm=26&gp=0.jpg"),),
                      flex: 2,
                    ),
                    Expanded(
                      child:Column(
                          children: <Widget>[
                            Image.network("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2484090652,3266104108&fm=26&gp=0.jpg",fit: BoxFit.fill,),
                            SizedBox(height: 10.0,),
                            Image.network("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2382576479,3688530667&fm=26&gp=0.jpg",fit: BoxFit.fill)
                          ],
                        ),
                      flex: 1,
                    )
                  ],
                ),
              )
            ],
          ),
        );
      }
    

    13、Stack、Align、Positioned组件

    Stack、Align、Positioned是用来定位布局使用的。
    Stack只能单一的组件进行定位
    Stack与其他两个组件同时使用就可以对多个组件进行定位

    13-1、Stack堆叠组件

    属性 描述 用法
    alignment 配置所有子元素显示的位置 alignment:AlignmentDirectional.topStart
    children 子元素集合 children:[]
        Stack(
          children: <Widget>[
            Container(width: 300.0,height: 400.0,color: Colors.red,),
            Container(width: 100.0,height: 100.0,color: Colors.black,),
            Text("测试测试测试测试测试测试",style: TextStyle(color: Colors.white),)
          ],
        );
    
    image.png

    结果发现子组件都堆叠在一起了

    alignment: Alignment.center

    Alignment.center会把所有的组件居中堆叠在一起``

    image.png

    Alignment自带很多方位,当自带的方位不满足我们的需求,我们可以自定义方位
    Alignment()他有两个参数X、Y 分别是0、1、-1之间的值
    Alignment(-1,-1)最左上角
    Alignment(-1,0) 贴近左侧垂直居中
    Alignment(-1,1) 贴近左侧垂直靠底部(左下角)
    Alignment(0,-1)横轴居中贴近顶部
    Alignment(0,0)居中
    Alignment(0,1)横轴居中贴近底部
    Alignment(1,-1)右上角
    当然也可以小数位

    单一使用Stack的缺点我们也知道了,没办法对子组件一一定位

    13-2、Stack与Align组件一起使用

    这样就可以实现我们类似前端的定位了

    Center(
          child: Container(//给了个最大的容器
            width: 400.0,
            height: 400.0,
            color: Colors.yellow,
            child: Stack(//然后使用Stack
              children: <Widget>[
                Align(//使用Align对子组件定位
                  alignment:Alignment.topRight,
                  child: Icon(Icons.home,size:40.0,color: Colors.red,)
                ),
                Align(
                  alignment:Alignment.bottomRight,
                  child: Icon(Icons.settings,size:40.0,color: Colors.blue,),
                ),
                Align(
                  alignment:Alignment(0,0),//我们使用实例化alignment:Alignment类
                  child: Icon(Icons.slow_motion_video,size:40.0,color: Colors.black,),
                ),
                
              ],
            ),
          ),
        );
    
    image.png

    13-3、Stack与Position一起使用(推荐使用这个)

    Position组件就比Align好用多了,他有left、top、bottom、right四个值

    Center(
          child: Container(
            width: 400.0,
            height: 400.0,
            color: Colors.yellow,
            child: Stack(
              children: <Widget>[
                Positioned(
                  left:50,
                  child: Icon(Icons.home,size:40.0,color: Colors.red,)
                ),
                Positioned(
                  left:150,
                  top: 100,
                  bottom: 20,
                  child: Icon(Icons.settings,size:40.0,color: Colors.blue,),
                ),
                Positioned(
                  left:250,
                  child: Icon(Icons.slow_motion_video,size:40.0,color: Colors.black,),
                ),
                
              ],
            ),
          ),
        );
    
    image.png

    14、AspectRatio组件

    AspectRatio定义子元素相对于父元素的宽高比例

    属性 描述 用法
    aspectRatio 定义子元素相对父元素的宽高比 aspectRatio:2.0/1.0
    child 子元素 child:Widget
    Container(
          width: 300.0,
          child: AspectRatio(
            aspectRatio: 2.0/1.0,
            child: Container(
              color:Colors.yellow
            ),
          ),
        );
    

    黄色区域的宽高比例是2/1


    image.png

    15、Card

    类似于前端中ElementUI中的Card差不多,我们可以给想要加阴影的容器套一层Card就有阴影效果了

    属性 描述 用法
    margin 外边距 margin: EdgeInsets.all(10.0),
    shadowColor 阴影颜色 shadowColor:Colors.red,
    elevation 阴影扩散的大小 elevation : 10.0
    return Contanier(width:200.0,height:200.0,child:Card());
    

    16、练习布局

    实现以下布局

    image.png
    class MyBody extends StatelessWidget{
      List list = new List();
    
      MyBody(){
        this.list = [{
          'avtorImg' : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1452751795,1897979528&fm=26&gp=0.jpg",
          'avtorName' : "十年之后_1",
          'avtorDesc' : "1描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",
          'backGround' : "https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3778173668,1422088699&fm=26&gp=0.jpg"
        },{
          'avtorImg' : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2405043393,1737103092&fm=26&gp=0.jpg",
          'avtorName' : "十年之后_2",
          'avtorDesc' : "2描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",
          'backGround' : "https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2335751370,711568964&fm=26&gp=0.jpg"
        },{
          'avtorImg' : "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3940043770,1553058007&fm=26&gp=0.jpg",
          'avtorName' : "十年之后_3",
          'avtorDesc' : "3描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",
          'backGround' : "https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1532844926,3671374399&fm=26&gp=0.jpg"
        },{
          'avtorImg' : "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2255910787,3354486640&fm=26&gp=0.jpg",
          'avtorName' : "十年之后_4",
          'avtorDesc' : "4描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",
          'backGround' : "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2155983538,3860699715&fm=26&gp=0.jpg"
        },{
          'avtorImg' : "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2611652800,2596506430&fm=26&gp=0.jpg",
          'avtorName' : "十年之后_5",
          'avtorDesc' : "5描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",
          'backGround' : "https://dss1.bdstatic.com/6OF1bjeh1BF3odCf/it/u=1242146115,2880436607&fm=74&app=80&f=JPEG&size=f121,90?sec=1880279984&t=0136ee81b657a616fbe11b994842e68e"
        }];
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return ListView.builder(
          itemCount: this.list.length,
          itemBuilder: (ctx,i){
            return Padding(
              padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
              child: Card(
                elevation:3.0,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    AspectRatio(aspectRatio: 10.0/5.0,child: Image.network(this.list[i]["backGround"],fit: BoxFit.cover,),),
                    Padding(
                      padding: EdgeInsets.fromLTRB(0,10.0,0,10.0),
                      child: ListTile(
                        leading: ClipOval(child: Image.network(this.list[i]["avtorImg"],width:50.0,height:50.0,fit: BoxFit.cover,),),
                        title: Text(this.list[i]["avtorName"],style: TextStyle(fontWeight: FontWeight.w600),),
                        subtitle: Text(this.list[i]["avtorDesc"],maxLines:1,overflow: TextOverflow.ellipsis,style: TextStyle(color: Color.fromRGBO(102, 102, 102, 1))),
                      )
                    )
                  ],
                )
              )
            );
          }
        );
      }
    }
    

    17、RaisedButton按钮组件

    如果需要给按钮宽高,那么可以通过给RaisedButton包裹一层Container设置宽高

    属性 描述 用法
    color 按钮的背景颜色 color:Colors.***
    textColor 按钮里面的文本颜色 textColor:Colors.pink
    child 子元素 child:Text()
    onPressed 点击事件 onPressed(){}/onPressed:自定义函数
    disabledTextColor 按钮禁用状态时文本颜色 disabledTextColor:Colors.pink
    disabledColor 按钮禁用状态时背景颜色 disabledColor:Colors.pink
    elevation 按钮的阴影 elevation:20.0
    shape 圆角、原型圆形按钮 看例子
    splashColor 点击按钮之后颜色变化过渡的效果 splashColor: Colors.red

    focusColor、hoverColor
    圆角:shape:RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)) )

    RaisedButton(
          padding: EdgeInsets.all(10.0),
          child: Text("按钮"),
          onPressed: (){},
          shape:RoundedRectangleBorder(//圆角按钮
                  borderRadius: BorderRadius.all(Radius.circular(25.0))
                ),
    
        );
    

    17-1圆形按钮

    使用shape属性,如果文本溢出,我们可以在包裹一层Container容器

    RaisedButton(onPressed: (){
                  print("圆形按钮");
                },child: Text("圆形按钮"),elevation: 20,splashColor: Colors.red,shape:CircleBorder(
                  side:BorderSide(color:Colors.white)
                ),)
    

    17-2图标按钮

    RaisedButton.icon()

        RaisedButton.icon(onPressed: (){
                print("图标按钮");
              }, icon: Icon(Icons.search), label: Text("图标按钮")),
    

    18、Wrap流式布局

    它类似与GridView.count,但是GridView.count需要定义一行显示几个,而且子元素宽度自动填满

    属性 描述 用法
    spacing 子元素之间的间距 spacing:20.0
    runSpacing 垂直之间的间距 runSpacing:20.0
    direction 主轴的方向,默认水平 direction:Axis.horizontal,
    alignment 主轴的对齐方式 alignment:WrapAlignment.start
    Wrap(
          spacing : 3.0,
          runSpacing : 20.0,
          direction:Axis.horizontal,
          children: <Widget>[
            MyButton("啊实打实群"),
            MyButton("啊实打"),
            MyButton("啊实打2352实群"),
            MyButton("实群"),
            MyButton("实群"),
            MyButton("啊实打2实群"),
            MyButton("啊实打实群3463"),
            MyButton("群"),
            MyButton("啊实43群"),
            MyButton("啊4群"),
          ],
        );
    
    image.png

    19、StatefulWidget

    当页面改变数据的时候我们需要使用StatefulWidget,例如当我们点击按钮,让文本的内容发生改变
    StatefulWidget中如果需要初始化值需要在initState方法中实现

     int _curderIndex = 0;
      List _navList = new List();
    
      @override
      void initState() {
        super.initState();
        this._navList = [
          
        ];
      }
    
    class HomePage extends StatelessWidget {
      String text = "你好Flutter1";
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Text(this.text),
            SizedBox(height: 30.0,),
            RaisedButton(
              child: Text("按钮"),
              onPressed: (){
                this.text = "asdq";
                print(this.text);
              },
            )
          ],
        );
      }
    }
    

    通过上述例子我们知道 文本不会发生变化但是值是变化了的。这说明在StatelessWidget不会改变组件的状态了

    19-1、定义一个StatefulWidget组件

    class HomePage extends StatefulWidget{
      HomePage(Key key) : super({key : key});//可以省略
      _HomePageState createState()=>_HomePageState ();
    }
    class _HomePageState extends State<HomePage>{
      @overirde
      Widget build(BuildContext context){
        return Text("1");
      }
    }
    

    StatefulWidget组件中有一个setState((){})的方法,可以改变组件的状态

    class _HomePageState extends State<HomePage>{
      int num = 0;
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Chip(label: Text("点击${num}次")),
            SizedBox(height: 30.0,),
            RaisedButton(child: Text("按钮"),
            onPressed: (){
              setState(() {//重点
                this.num ++;
              });
            })
          ],
        );
      }
    }
    

    通过动态组件我们写一个案例:点击按钮增加一条数据

    class _HomePageState extends State<HomePage>{
      int num = 0;
      List list = new List();
      
      @override
      Widget build(BuildContext context) {
        return ListView(
          children: <Widget>[
            Column(
              children: this.list.map((e){**重点**
                return Text(e);
              }).toList(),
            ),
            SizedBox(height:30.0),
            RaisedButton(
              child: Text("按钮"),
              onPressed: (){
                setState((){**重点**
                  this.num ++;
                  this.list.add("${this.num}条数据");
                });
              }
            )
          ],
        );
      }
    }
    
    image.png

    20、BottomNavigationBar自定义底部导航栏组件,以及页面切换

    BottomNavigationBar底部导航栏组件,它是属于Scaffold组件中的属性

    属性 值类型 说明
    items 一个为BottomNavigationBarItem的集合 items:[ BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首页"))]
    onTap 点击导航栏的回调事件 onTap:(index){print(index);},
    currentIndex 默认选中的下标 currentIndex:2
    type 导航栏的类型:fixed、shifting type:BottomNavigationBarType.shifting
    fixedColor 底部导航栏type为fixed时导航栏的颜色,如果为空的话默认使用ThemeData.primaryColor fixedColor:Colors.red
    iconSize 导航栏图片的大小 iconSize:23
    bottomNavigationBar: BottomNavigationBar(
              currentIndex : 1,
              onTap:(i){},
              items: [
                BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首页")),
                BottomNavigationBarItem(icon: Icon(Icons.list),title: Text("分类")),
                BottomNavigationBarItem(icon: Icon(Icons.settings),title: Text("设置")),
              ],
            ),
    

    20-1、设置点击导航栏切换页面/选中状态

    我们知道改变状态需要在StatefulWidget中实现,那么我们把整个Scaffold单独抽出成一个组件

    class HomePage extends StatefulWidget{
      HomePage({Key key}):super(key:key);
      @override
      _HomePageState createState()=>_HomePageState();
    }
    
    class _HomePageState extends State<HomePage>{
      int index = 0;
      @override
      Widget build(BuildContext context) {
        
        return Scaffold(
            appBar: AppBar(title: Text("页面定位布局"),),
            body: MyBody(),
            bottomNavigationBar: BottomNavigationBar(
              currentIndex : this.index,
              onTap:(i){
                setState(() {
                  this.index = i;
                });
              },
              items: [
                BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首页")),
                BottomNavigationBarItem(icon: Icon(Icons.list),title: Text("分类")),
                BottomNavigationBarItem(icon: Icon(Icons.settings),title: Text("设置")),
              ],
            ),
            drawer: Drawer(
              child: ListView(
                children: <Widget>[
                  DrawerHeader(child: Text("Drawer"),decoration: BoxDecoration(color:Colors.pink),),
                  ListTile(leading: Icon(Icons.settings),title: Text("设置"),),
                ],
              ),
            ),
          );
      }
    }
    

    20-2、利用bottomNavigationBar切换页面

    我们在lib中新建一个pages文件夹,然后创建几个简单的Widget

    import 'package:flutter/material.dart';
    import './tabs/home.dart';
    import './tabs/list.dart';
    import './tabs/seting.dart';
    
    
    class Tabs extends StatefulWidget{
      Tabs({Key key}) : super (key:key);
    
      @override
      _TabsState createState()=>_TabsState();
    }
    
    class _TabsState extends State<Tabs>{
    
      int _curInedex = 0;
    
      List _pageList = [//页面数组
        HomePage(),
        ListPage(),
        SetingPage()
      ];
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return Scaffold(
          appBar: AppBar(title:Text("导航栏的使用,切换")),
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: this._curInedex,
            onTap: (i){
              setState((){
                this._curInedex = i;
              });
            },
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首页")),
              BottomNavigationBarItem(icon: Icon(Icons.line_weight),title: Text("分类")),
              BottomNavigationBarItem(icon: Icon(Icons.settings),title: Text("设置")),
            ],
          ),
          drawer: Drawer(
            child: ListView(
              children: <Widget>[
                DrawerHeader(child: Text("抽屉组件"),decoration:BoxDecoration(color:Colors.pink)),
                ListTile(leading: Icon(Icons.settings),title: Text("设置"),)
              ],
            ),
          ),
          body: this._pageList[this._curInedex],重点
        ); 
      }
    }
    
    

    21、Flutter中的路由

    Navigator.of()跳转某一页面,Navigator.pop()回到上一页面

    Navigator.of(context).push(
      MaterialPageRoute(builder:()=>页面组件())
    )
    

    21-1、页面跳转传值

    类似于vuerouter传值
    非常简单,我们知道构造函数是可以传参的而我们的builder :()=>页面组件(这里是可以传参的)

    Navigator.of(context).push(
      MaterialPageRoute(builder:()=>MyPage(title:"标题"));
    )
    
    class MyPage extends StatelessWidget{
      String title = "";
      MyPage({title:this.title})
      ...
    }
    

    22-2、路由命名跳转

    使用Navigator.pushNamed()

    如果要使用pushNamed方法那么需要在MaterialApp中定义好路由

      MaterialApp(
        title: "路由跳转",
        theme: ThemeData(primaryColor: Colors.pink),
        routes: {//这里的key可以随意起
          "/form":(BuildContext  context)=>FormPage(),
          "/serach":(BuildContext  context)=>SerachPage()
        },
        home: MyScaffold()
      );
      按钮中实现路由跳转
      onPressed:(){
        Navigator.pushNamed(context, "/serach");
      }
    

    22-3、命名路由传值、路由验证

    命名路由传值不能像构造参数传值那样,需要指定onGenerateRoute属性Flutter中文网onGenerateRoute属于MaterialApp的属性

    路由拦截验证

    假设我们要开发一个电商APP,当用户没有登录时可以看店铺、商品等信息,但交易记录、购物车、用户个人信息等页面需要登录后才能看。

    首先我们定义路由Map
    案例:我们超前使用本地存储功能获取userName
    MaterialApp(
      routes: {
        "/login" : (BuildContext context)=>LoginPage(),
        "/home" :(BuildContext context)=>HomePage(), 
        "/card" :(BuildContext context)=>CardPage(), 
        ...
      },
      "onGenerateRoute":(RouteSettings setings){
        final String routeName = settings.name;//获取进入的路由名字,
        final prefs = await SharedPreferences.getInstance(); //本地存储获取
        final userName = prefs.getString("userName");
        if(routeName !="/login" && !userName.isEmpty){//如果用户名不存在那么我们跳转至登录页面
           Navigator.pushNamed(context, "/login");
        }
      }
    );
    

    命名路由传参

    这里有个巨坑,如果此时MaterialApp还配置routes属性会报错;
    切记,切记,切记

    final _routes = {
        "/login" : (BuildContext context)=>LoginPage(),
        "/home" :(BuildContext context)=>HomePage(), 
        "/card" :(BuildContext context,{arguments})=>CardPage(infoCard:arguments),
        ...
      },
    MaterialApp(
      "onGenerateRoute" : (RouteSettings  setings){
        final String name = settings.name;
        final Function pageBuilder = this._routes[name];
        if (pageBuilder != null) {
          if (settings.arguments != null) {
            // 如果透传了参数
            return MaterialPageRoute(
                builder: (context) =>
                    pageBuilder(context, arguments: settings.arguments));
          } else {
            // 没有透传参数
            return MaterialPageRoute(builder: (context) => pageBuilder(context));
          }
        }
      }
    )
    然后我们需要在 CardPage页面中接收`infoCard`参数
    class CardPage extends StatelessWidget{
      final infoCard;
      CardPage({this.infoCard});
      ...
    }
    

    配置根路由

    我们开发Vue项目知道,一般会配置一个根路由
    flutter中配置跟路由使用initialRoute属性,该属性属于MaterialApp,当使用了该属性时,在使用home属性可能会导致混乱

    MaterialApp(
      initialRoute:"/home"
    )
    

    22-4、替换根路由

    Navigator.pushReplacementNamed();
    Navigator.of(context).pushAndRemoveUntil;
    使用场景:当我们跳转页面中在跳转页面,例如当我们点击注册按钮进入注册页面,然后输入完信息之后,点击下一步,跳转第二步注册,然后点击注册完成按钮,返回的页面应该不是第一步中的注册页面,而是其实页面

          Column(
               mainAxisAlignment: MainAxisAlignment.center,
               children: <Widget>[
                 Text("请输入手机号,密码,点击下一步继续完成注册操作"),
                  RaisedButton(onPressed: (){
                    //进入第二步骤注册页面
                    Navigator.pushReplacementNamed(context, "/regSend");
                  },child: Text("下一步"),)
               ],
             )
    
          Column(
               mainAxisAlignment: MainAxisAlignment.center,
               children: <Widget>[
                 Text("注册完成"),
                  RaisedButton(onPressed: (){
                    //此时pop返回的不是第一步注册页面了,而是其实页面了
                    Navigator.of(context).pop();
                  },child: Text("完成注册"),)
               ],
             )
    

    如果页面嵌套很多层,而每次都使用Navigator.pushReplacementNamed();保存根确实有些小麻烦,那么我们可以使用pushAndRemoveUntil方法,不管你嵌套多少层,直接使用该方法直接可返回跟路由

          Navigator.of(context).pushAndRemoveUntil(
            //这个案例是返回tab页面
            new MaterialPageRoute(builder: (context)=>Tabs(index:1)),
                (route)=>route == null);
    

    23、floatingActionButton浮动按钮

    floatingActionButton是属于Scaffold中的属性

    floatingActionButton: FloatingActionButton(onPressed: (){
            Navigator.pop(context);//路由返回
          },child: Text("返回"),),
    

    23-1 控制floatingActionButton的显示位置

    floatingActionButtonLocation属性该属性是在Scaffold中的,
    floatingActionButton是同级关系

    Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: (){},child: Icon(Icons.add),),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,//底部中间
    )
    
    

    23-2实现底部导航的凸起按钮

    思路:我们可以定义5个bottomNavigationBar然后使floatingActionButton盖住中间的那一个然后点击事件我们改变curIndex的值即可

    image.png
    Scaffold(
      floatingActionButton: Container(
            width: 62.0,
            height: 62.0,
            padding: EdgeInsets.all(3.0),//padding
            decoration: BoxDecoration(
              shape:BoxShape.circle,//圆形
              color: Colors.white,//白色
              boxShadow:[BoxShadow(color: Colors.black26, offset: Offset(0, 0),blurRadius: 2.0, spreadRadius: 0.0)]//阴影
            ),
            child: FloatingActionButton(onPressed:(){
              setState(() {
                this.curIndex = i;
              });
            },splashColor: null,elevation:0,child: Icon(Icons.add,size: 40.0,color: Color.fromRGBO(51, 51, 51, 1),),backgroundColor: Colors.yellow,),
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("首页")),
              BottomNavigationBarItem(icon: Icon(Icons.list),title: Text("分类")),
              BottomNavigationBarItem(icon: Icon(Icons.text_fields),title: Text("卖闲置")),
              BottomNavigationBarItem(icon: Icon(Icons.shopping_cart),title: Text("购物车")),
              BottomNavigationBarItem(icon: Icon(Icons.people),title: Text("我的")),
            ],
            type:BottomNavigationBarType.fixed,
            currentIndex: this.curIndex,
            onTap: (i){
              setState(() {
                this.curIndex = i;
              });
            },
          ),
    )
    
    
    image.png

    24、SingleChildScrollView

    当我们软键盘弹起的时候,会盖住我们的内容,这是就会报越界,使用SingleChildScrollView页面会随高度改变而又滚动条

    25、自定义AppBar以及Tab切换

    在使用AppBar我们之前一直都是直接title属性带过,现在我们仔细看看它有哪些属性

    属性 描述 用法
    title 标题 title:Text("标题")当然也可以不是Text
    leading AppBar左侧组件 leading:Icon(Icons.back)当然也可以不是Icon
    actions 右侧组件集合 actions:[Icon(Icons.back),Icon(Icons.back)]
    backgroundColor Appbar背景颜色 backgroundColor:Colors.blue
    bottom 通常放TabBar,标题下面放一栏tab切换 bottom:TabBar(tabs: [ ])
    iconTheme appbar中的图标统一样式 iconTheme: IconThemeData(color: Colors.pink,opacity:0.3,size:20),
    textTheme 文字样式 textTheme : TextTheme(headline1:TextStyle(color: Colors.black))
    centerTitle 标题是否居中 centerTitle:true
      Scaffold(
          appBar : AppBar(
            leading: IconButton(icon: Icon(Icons.pin_drop), onPressed: (){
              //这里我们使用了IconButton组件图标按钮点击返回根组件
              Navigator.pushNamed(context, "/");
            }),
            backgroundColor: Colors.blue,
            title: Text("AppBarDemo"),
            actions: [//右侧组件集合
              Icon(Icons.home),
              Text("电话")
            ],
            centerTitle : true,//标题居中
            iconTheme: IconThemeData(color: Colors.pink,opacity:1,size:30),
            textTheme : TextTheme(headline1:TextStyle(color: Colors.black),overline:TextStyle(color: Colors.black))
          )
        )
    
    image.png

    25-1Tab切换

    进行Tab切换在Scaffold组件中需要包裹一层DefaultTabController组件

    TabBar常用属性

    属性 描述 用法
    tabs tab的内容 tabs: [Tab(text: "tab1"),Tab(text: "tab2",) ]
    controller TabController对象 controller:TabController()
    isScrollable 是否可以滚动,当有多个Tab选项时就会被挤在一起,设置为true可以解决 isScrollable:false
    indicatorColor 指示器颜色 indicatorColor:Colors.blue
    indicatorWeight 指示器高度 indicatorWeight:2
    indicatorPadding 指示器的padding indicatorPadding:EdgeInsets.all(10),
    indicator 指示器的描述样式 indicator: Decoration(***)
    labelColor 文字的颜色 labelColor:Colors.blue
    labelStyle 文字的样式 labelStyle:TextStyle()
    labelPadding 文字的padding labelPadding:EdgeInsets.all(10)
    unselectedLabelColor 未选中的文字颜色 unselectedLabelColor:Colors.pink
    unselectedLabelStyle 未选中的文字样式 lunselectedLabelStyle:TextStyle()
    DefaultTabController(
      length:2,//定义有几个tab切换
      child:Scaffold(
        appBar:AppBar(
          title:Text("自定义Tab切换"),
           bottom:TaBar(
            tabs:[
              Tab(text:"音乐",icon:Icon(Icons.headset)),
              Tab(text:"推荐"),
            ],//tab切换
          ),
        ),
        body:TabBarView(//主内容需要用TabBarView包括
          children:[
            Center(child:Text("对应音乐")),//顺序对应bottom中的tabs顺序
            Center(child:Text("对应推荐")),
          ]
         )
      )
    )
    
    image.png

    这个是单独页面定义自动以Tab切换,如果我们要在底部导航栏页面增加Tab切换怎么做呢。因为我们底部导航栏页面是分别抽出来公共Scaffold组件的,如果被DefaultTabController包裹,那么所有的底部导航栏页面都会增加Tab切换

    Scaffold中可以继续嵌套Scaffold

    DefaultTabController(length: 2, child: Scaffold(
          appBar: AppBar(
            leading: Icon(null),
            centerTitle: true,
            title: Row(//这里title我们改成了TaBar
              children: <Widget>[
                Expanded(child: TabBar(labelColor:Colors.blue,tabs: [
                  Tab(text: "tab1"),
                  Tab(text: "tab2",)
                ]))
              ],
            ),
          ),
          body: TabBarView(children: [
            Column(
              mainAxisAlignment:MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Text("页面传值跳转,类似于vue、Router跳转传值"),
                RaisedButton(onPressed: (){
                  Navigator.of(context).push(
                    MaterialPageRoute(builder: (context)=>FormPage(formInfo:{'id':"传值的12"}))
                  );
                },child: Text("传值跳转"),)
              ],
            ),
            Center(child:Text("第二个tab2"))
          ]),
        )
        )
    
    image.png

    25-2 自定义TabBar

    为什么我们要自定义呢?为什么不用上述的例子呢?用上述的例子我们无法监听tab切换的事件,而自定义我们可以监听
    如果要使用自定义TabBar的话,组件必须是StatefulWidget动态组件并且需要实现SingleTickerProviderStateMixin

    class TabBarController extends StatefulWidget{
     _TabBarControllerState createState()=> _TabBarControllerState();
    }
    //这里要实现SingleTickerProviderStateMixin
    class _TabBarControllerState extends State<TabBarController> with SingleTickerProviderStateMixin{
      TabController _tabController;
      @override
      void initState(){
        this._tabController = TabController(length:2,vsync:this);
        this._tabController.addListener((){//监听切换事件
          print(this._tabController.index);//切换的下标
        });
      }
      @override
      Widget build(BuildContext context) {
        return Scaffold{
          appBar:AppBar(
            title:Text("TabBarController"),
            bottom:TabBar(tabs:[
                Tab(child:Text("tab1")),
                Tab(child:Text("tab2")),
              ], 
              controller:this._tabController//别忘了这一步
            )
          ),
          body:TabBarView(
            children : [
              Center(child:Text("切换至Tab1")),
              Center(child:Text("切换至Tab2")),
            ],
            controller:this._tabController//别忘了这一步TabBarView也需要加上
          )
        }
      }
    }
    

    26、Draw抽屉侧边栏

    drawer是属于Scaffold中的属性左侧侧边栏
    endDrawer是属于Scaffold中的属性右侧侧边栏

    Scaffold(
      drawer: Drawer(
            child: ListView(
              children: <Widget>[
                DrawerHeader(child: Text("抽屉组件"),decoration:BoxDecoration(color:Colors.pink)),
                ListTile(leading: Icon(Icons.settings),title: Text("设置"),)
              ],
            ),
          ),
      endDrawer : Drawer(
            child: ListView(
              children: <Widget>[
                DrawerHeader(child: Text("抽屉组件"),decoration:BoxDecoration(color:Colors.pink)),
                ListTile(leading: Icon(Icons.settings),title: Text("设置"),)
              ],
            ),
          )
    )
    
    image.png

    26-1使用UserAccountsDrawerHeader在侧边栏显示用户信息

    属性 描述 用法
    accountName 用户名 accountName:Text("十年之后")
    accountEmail 可以称为用户描述寄语,官方是邮箱的意思 accountEmail:Text("简书写你所想")
    currentAccountPicture 用户头像图片 currentAccountPicture:CircleAvatar(backgroundImage: NetworkImage(***)))
    drawer: Drawer(
            child: ListView(
              children: <Widget>[
                UserAccountsDrawerHeader(
                  accountName: Text("十年之后"), accountEmail: Text("简书,写你所想"),
                  currentAccountPicture:CircleAvatar(backgroundImage: NetworkImage("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2586537036,779451914&fm=26&gp=0.jpg"),radius: 25.0,)
                ),
                ListTile(
                  leading: Icon(Icons.settings),
                  title: Text("设置"),
                )
              ],
            ),
          ),
    

    27 ButtonBar按钮组

    ButtonBar里面放N个按钮

    属性 描述 用法
    children 按钮组 children:[]
    alignment 按钮组的对齐方式 alignment:MainAxisAlignment.start,
    ButtonBar(
                  alignment:MainAxisAlignment.start,
                  children: <Widget>[
                    FlatButton(onPressed: (){},child: Text("扁平化按钮"),color:Colors.blue),
                    
                  ],
                )
    

    28表单

    28-1TextFaild

    输入表单类似input

    属性 描述 应用
    decoration - hintText 类似于前端placeholder提示信息 decoration:InputDecoration(hintText:"输入名字")
    decoration - border 给输入框增加边框 decoration:InputDecoration(border:OutlineInputBorder())
    maxLength 限制输入框输入的字符长度 maxLength:4限制只能输入4个字符
    maxLines 多行文本,类似于textarea maxLines:4限制4行
    obscureText 密码框 obscureText:true
    controller 输入框里面输入的值 controller:TextEditingController(text:'十年之后')
    decoration - labelText labelText decoration:InputDecoration(labelText:"用户名")
    decoration - icon 表单图标 decoration:InputDecoration(icon: Icon(Icons.home))
    onChanged 监听输入框文本改变的事件 onChanged:(val){print(val);}
    TextField(
      obscureText : true,//密码
      maxLength : 14,//只能输入14个字符
      maxLines : 1,//只有一行
      decoration : InputDecoration(
        border:OutlineInputBorder(),//边框
        labelText:"密码",
        icon:Icon(Icons.format_indent_increase)
      )
    )
    
    image.png

    28-1-1、TextFaild获取输入的值/修改值

    利用TextFaild中的controller属性我们绑定值

    var user_name = TextEditingController();
    @initState(){
      user_name.text = "十年之后初始值";注意这里修改的是text的属性的值
    }
    TextField(
        controller:user_name,这里赋值
        decoration: InputDecoration(
        labelText: "用户名",
        contentPadding: EdgeInsets.all(3),
        border: OutlineInputBorder()
      ),
    )
    

    点击按钮获取表单输入的值

    TextField(
        controller:user_name,这里赋值
        decoration: InputDecoration(
        labelText: "用户名",
        contentPadding: EdgeInsets.all(3),
        border: OutlineInputBorder()
      ),
    )
    RaisedButton(onPressed: (){
       print(this.user_name.text);注意这里是获取user_name中的text
    },child: Text("获取表单的值"),)
    

    点击修改输入框的值

    RaisedButton(onPressed: (){
      setState(() {
        this.user_name.text = "十年之后Dart与Flutter";
      });
    },child: Text("修改表单的值"),),
    

    28-2 CheckBox多选框

    属性 描述 应用
    value 绑定的值 value:flag事先定义flag
    onChanged 监听状态改变 onChanged:(val){setState((){flag=val;})}
    activeColor 选中时的颜色 activeColor:Colors.blue
    var flag = false;
    Row(children:[
      Checkbox(value: flag, onChanged: (val){
       setState(() {
         this.flag = val;
       });
      }),
      Text(this.flag?"选中":"未选中")
    ])
    

    28-2-1 CheckBoxListTitle

    属性 描述 用法
    selected 选中高亮 selected:判断当前枚举值是否等该枚举值就高亮

    CheckBoxListTitleListTitle类似

    CheckboxListTile(value: flag,onChanged: (val){
     setState(() {
      flag = val;
      });
     },title:Text("同意协议"),subtitle: Text("23456"),),
    
    image.png

    28-3 Radio单选框

    Radio往往是成对出现的,怎么保证多个Radio是一个组呢?
    这时候我们要用groupValue属性给多个Radio绑定同一个值

    属性 描述 应用
    value 该单选框的枚举值 value:1写上你的枚举值不用是变量
    groupValue 绑定的值 groupValue:this.sex事先定义sex
    onChanged 监听改变事件 onChanged:(val){setState((){sex=val;})}
                 var sex = 1;
                Row(
                  children: <Widget>[
                    Text("男:"),
                    Radio(value: 1, groupValue: this.sex, onChanged: (v){
                      setState(() {
                        this.sex = v;
                      });
                    }),
                    Text("女:"),
                    Radio(value: 2, groupValue: this.sex, onChanged: (v){
                      setState(() {
                        this.sex = v;
                      });
                    })
                  ],
                )
    

    28-3-1、RadioListTitle

    属性 描述 用法
    selected 选中高亮 selected:判断当前枚举值是否等该枚举值就高亮

    CheckBoxListTitle类似

    RadioListTile(value: 1, groupValue: this.a, onChanged: (v){
                        setState(() {
                          this.a = v;
                        });
                      },title: Text("爬山"),subtitle: Text("2020-06-02"),selected:this.a==1),
                RadioListTile(value: 2, groupValue: this.a, onChanged: (v){
                        setState(() {
                          this.a = v;
                        });
                      },title: Text("游泳"),subtitle: Text("2020-06-02"),selected:this.a==2),
    
    image.png

    28-4、Switch开关

    Switch(value: b, onChanged: (v){
                  setState(() {
                    b =v;
                  });
                })
    

    28-4-1 SwitchListTitle

    SwitchListTile(value: b, onChanged: (v){
                    setState(() {
                      b =v;
                    });
                  },title: Text("开关"),subtitle: Text("描述"),)
    

    28-5、练习表单

    image.png

    重点是利用数组循环map渲染兴趣

    Scaffold(
            appBar: AppBar(title: Text("formDemo练习"),),
            body: Container(
                padding: EdgeInsets.all(20.0),
                child:  Column(
                    children: <Widget>[
                        TextField(
                            controller: this.user_name,
                            onChanged: (v){
                                setState(() {
                                  this.user_name.text = v;
                                });
                            },
                            decoration: InputDecoration(
                                labelText: "用户名",
                                border: OutlineInputBorder(),
                                contentPadding: EdgeInsets.all(3.0)
                            ),
                        ),
                        SizedBox(height: 10.0,),
                        Row(
                            children: <Widget>[
                                Text("性别"),
                                Radio(value: true, groupValue: this.sex, onChanged: this._setSex),
                                Text("男"),
                                Radio(value: false, groupValue: this.sex, onChanged: this._setSex),
                                Text("女"),
                            ],
                        ),
                        SizedBox(height: 10.0,),
                        Row(
                            children: this._listCheck(),
                        ),
                        SizedBox(height: 10.0,),
                        TextField(
                            controller: this.user_info,
                            maxLines: 4,
                            decoration: InputDecoration(
                                border: OutlineInputBorder(),
                                hintText:"用户信息"
                            ),
                            onChanged: (v){
                                setState(() {
                                  this.user_info.text = v;
                                });
                            },
                        ),
                        SizedBox(height:10.0),
                        RaisedButton(onPressed: (){},child: Text("提交"),color: Colors.blue,textColor: Colors.white,)
                    ],
                )
            )
        );
    

    29、DateTime时间、时间组件

    29-1、获取当前时间

    DateTime.now()

    print(DateTime.now())  2020-06-03 15:06:30.832249
    

    29-2、获取时间戳

    millisecondsSinceEpoch

    print(DateTime.now().millisecondsSinceEpoch) 1591167990832
    

    29-3、时间戳转化成日期对象

    DateTime.fromMillisecondsSinceEpoch(时间戳)

    DateTime date = DateTime.now() 获取当前时间
    int timeStamp = date.millisecondsSinceEpoch;
    print(DateTime.fromMillisecondsSinceEpoch(timeStamp))
    

    29-4、转化成指定的年月日

    使用第三方库date_format地址

    1、在项目的跟目录pubspec.yaml中追加包

    dependencies:
      date_format: ^1.0.8
    

    2、在需要用到插件的文件中添加

    import 'package:date_format/date_format.dart';
    

    3、使用

    DateTime date = DateTime.now();
    print(formatDate(date, [yyyy, '年', mm, '月', dd,'日',' ',HH,':',nn,':',ss]));
    2020年06月03日 15:23:43
    

    29-5、flutter自带的日期组件

    image.png

    使用日期组件并获取选中后的值

    DateTime date = DateTime.now();//获取当前日期
    void _showDatePicker() async{//自定义显示日期方法 
     var result =  await showDatePicker(
        context : context,//上下文
        initialDate:date,//默认选中的日期
        firstDate:DateTime(1980),//日期组件的起始日期我们定在1980年
        lastDate:DateTime(2050),//日期组件的结束日期
      );
      print(result );//这个就是我们选中之后的值注意该方法为异步方法
      setState((){
        this.date = result;
      })
    }
    //然后我们在点击事件中调用这个自定义方法
    InkWell(
      onTap:(){
        this._showDatePicker()//调用日期控件
      },
      child:Text("显示日期组件")
    )
    

    29-6、flutter自带的时间控件

    image.png
    DateTime time = DateTime.now();//获取当前日期
    void _showTimePicker() async{//自定义显示日期方法 
     var result = await showTimePicker(
          context: context, 
          initialTime: TimeOfDay(hour:time.hour,minute:time.minute)//重点哦
      );
      print(result );//这个就是我们选中之后的值注意该方法为异步方法
      setState((){
        this.time = result;
      })
    }
    //然后我们在点击事件中调用这个自定义方法
    InkWell(
      onTap:(){
        this._showTimePicker()//调用日期控件
      },
      child:Text("显示时间组件")
    )
    

    29-7、怎么把日期、时间控件转为中文

    要用到flutter中的国际化,具体可以查看Flutter 中的国际化

    29-8、第三方日期、时间组件

    flutter_datetime_picker

      DatePicker.showDatePicker(context,
        showTitleActions: true,
        minTime: DateTime(2018, 3, 5),
        maxTime: DateTime(2019, 6, 7), onChanged: (date) {
          print('change $date');
        }, onConfirm: (date) {
         print('confirm $date');
        }, 
      currentTime: DateTime.now(), 
      locale: LocaleType.zh
    );
    

    30、InkWell组件

    效果:给一些没有点击事件的组件增加事件的,
    例如如果我们要给Text增加点击事件,以往我们只能套子button中,但现在不用

    InkWell(
      child:Text("监听点击事件"),
      onTap:(){
        print(DateTime.now())
      }
    )
    

    31、第三方轮播图插件

    flutter_swiper
    Swiper需要包裹在Container中,并且需要设置宽高不然报错

    Swiper(
            itemBuilder: (BuildContext context,int index){
              return new Image.network("http://via.placeholder.com/350x150",fit: BoxFit.fill,);
            },
            itemCount: 3,
            pagination: new SwiperPagination(),
            control: new SwiperControl(),
          ),
        );
    

    32、各种DiaLog弹出层

    AlertDialogSimpleDialogshowModalBottomSheet
    这些DiaLog需要放在showDialog方法中
    关闭遮罩Navigator.pop()

    32-1、AlertDialog

    void _showAlertDiaLog() async{
        var result = await showDialog(context: context,builder: (context){
          return AlertDialog(
            title: Text("提示信息"),
            content: Text("您确定要删除么?"),
            actions: <Widget>[
              RaisedButton(onPressed: (){
                print("确定");
                Navigator.pop(context,'传值给result');传值出去
              },child: Text("是"),),
              RaisedButton(onPressed: (){
                print("否");
                Navigator.pop(context);
              },child: Text("否"),)
            ],
          );
        });
        print(result);
      }
    
    image.png

    32-2、SimpleDialog

    void _showSimpleDiaLog()async {
        var result = await showDialog(context: context,builder: (context){
          return SimpleDialog(
            title: Text("提示信息"),
            children: [
              SimpleDialogOption(
                onPressed:(){
                  Navigator.pop(context,'测试');
                },
                child: Text("测试"),
              ),
              Divider(),
              SimpleDialogOption(
                onPressed:(){
                  Navigator.pop(context,'测试1');
                },
                child: Text("测试1"),
              ),
              Divider(),
              SimpleDialogOption(
                onPressed:(){
                  Navigator.pop(context,'测试2');
                },
                child: Text("测试2"),
              ),
              Divider(),
              //当然也可以是其他的组件
              InkWell(
                
                onTap: (){
                  Navigator.pop(context,'InkWell组件');
                },
                child: Text("InkWell组件"),
              )
            ],
          );
        });
        print(result);
      }
    
    image.png

    32-3、showModalBottomSheet类似于购物车/时间选择器底部弹出框

    他不用使用showDialog来弹出

    image.png
    void _showShowModalBottomSheet(){
        showModalBottomSheet(
          context: context,
          builder: (context){
            return Container(
              height: 250.0,//控制弹出的高度
              child: Column(
                children: <Widget>[
                  ListTile(
                    title: Text("分享A"),
                    onTap: (){
                      Navigator.pop(context);
                    },
                  ),
                  Divider(),
                  ListTile(
                    title: Text("分享B"),
                    onTap: (){Navigator.pop(context);},
                  ),
                  Divider(),
                  ListTile(
                    title: Text("分享C"),
                    onTap: (){Navigator.pop(context);},
                  ),
                ],
              ),
            );
          }
        );
      }
    

    32-4、使用第三方插件toast

    image.png

    fluttertoast

    dependencies:
      fluttertoast: ^4.0.1
    
    import 'package:fluttertoast/fluttertoast.dart';
    
    void _showShowToast(){
          Fluttertoast.showToast(
            msg: "提示信息",
            gravity: ToastGravity.BOTTOM,
            timeInSecForIosWeb: 1,//设置停留时间 只在IOS端有效
            backgroundColor: Colors.black,
            textColor: Colors.white,
            fontSize: 16.0
          );
      }
    

    33、flutter中网络请求

    33-1、JSON字符串与flutter中的Map类型相互转换

    需要调用import 'dart:convert';
    jsonDecode 字符串json转为Map
    jsonEncodeMap转为字符串

    String list_json = '{"result":"测试","code":0}';
    jsonDecode(list_json ); = >转为Map
    Map map = {"result":"测试","code":0};
    jsonEncode(map );
    

    技巧

    container圆形/阴影

    Container(
      width:50.0,
      height:50.0,
      decoration:BoxDecoration(
        color:Colors.yellow,//背景图片
        shape:BoxShape.circle,//圆形
        boxShadow:[BoxShadow(color: Colors.black, offset: Offset(0, 0),blurRadius: 2.0, spreadRadius: 0.0)]//阴影
      )
    )
    

    width/height相对于父容器100%

    double.infinity

    container(
      height:double.infinity,
      height:double.infinity,
    )
    

    总结目前学的组件有哪些

    名称 描述
    MaterialApp 根组件
    Scaffold 根组件中的home
    Text 文本组件
    Container 容器组件
    ListView 列表组件
    ListView.builder 列表组件中的循环动态生成
    SizedBox 这个组件目的撑开组件与组件之间的距离
    GridView.count()/GridView.builder() 栅格化布局
    Padding 内边距组件EdgeInsets.only(bottom: 10.0)仅bottom增加边距
    Row 横轴排列组件
    Cloumn 纵轴排列组件
    Expanded Flex布局组件
    Image 图片组件
    Icon 图标组件
    Stack 对子元素统一定位
    Align Stack一起配合使用,对多个子元素进行定位
    Position Stack一起配合使用,对多个子元素进行定位
    AspectRatio 定义子组件与父元素的宽高比例
    Card 卡片,可以给容器加阴影效果
    ClipOval 圆形组件
    CircleAvatar 圆形组件含有backgroundImage
    Wrap 流式布局,当子元素横轴铺满时自动换行至第二行
    RaisedButton 凸起按钮
    BottomNavigationBar 导航栏属于Scaffold中的参数/属性
    floatingActionButton 浮动底部按钮导航栏属于Scaffold中的参数/属性,可以实现底部凸起导航按钮
    SingleChildScrollView 解决键盘弹起覆盖内容而引起的越界错误
    IconButton 图标按钮,注意此图标按钮不带字
    RaisedButton.icon 带字的图标按钮
    FlatButton 扁平化按钮,不带边框,不带阴影
    OutlineButton 只带边框的按钮,没有背景色
    BottonBar 按钮组
    floatingActionButtonLocation 不是button控制floatingActionButton的位置
    TextFaild 输入框
    Divider() 直接使用效果是一根线条/分割线
    CheckBox 复选框
    CheckBoxListTitle() 带标题的复选框,可以包裹Container控制宽高
    Radio() 单选框
    RadioListTitle() 带标题的单选框,可以包裹Container控制宽高
    Switch() 开关
    SwitchListTitle() 带标题的开关,可以包裹Container控制宽高
    InkWell 给一些没有点击事件的组件增加监听点击事件
    showDiaLog() 用来显示弹出框
    AlertDiaLog() 提示框
    SimpleDialog() 提示框1
    showModalBottomSheet() 底部弹出框/类似日期控件

    相关文章

      网友评论

        本文标题:Drat与Flutter基础

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