美文网首页Flutter
Flutter常见知识点汇总

Flutter常见知识点汇总

作者: StevenHu_Sir | 来源:发表于2019-07-24 21:01 被阅读0次

    1.主题色主题色设置

    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Flutter Demo',//针对Android 里面可用
          theme: ThemeData(
            primarySwatch: Colors.yellow,//主题色设置,深色:则时间和电池颜色为白色;浅色:则时间和电池颜色为黑色
            highlightColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
            splashColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
          ),
          home: RootPage(),
        );
      }
    }
    

    2.ListView去掉iPhoneX刘海

    MediaQuery.removePadding -> removeTop: true

    Container(
      color: Color.fromRGBO(220, 220, 220, 1.0),
      child: MediaQuery.removePadding(
        removeTop: true,
        context: context,
        child: ListView(
          children: <Widget>[
            Container(
              color: Colors.white,
              height: 200,
            ),
            SizedBox(height: 10,),
            DiscoverCell(imageName: 'images/微信支付1.png',title: '支付',),
          ],
        ),
      ),
    ),
    

    3.Image设置圆角

    Row(
    children: <Widget>[
      Container(
        width: 70,
        height: 70,
       // child: Image(image: AssetImage('images/Steven.png'),),//写在此处设置圆角无效
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(10.0),
          image: DecorationImage(image:AssetImage('images/Steven.png'),
          fit: BoxFit.cover)//设置图片的填充模式
        ),
      ),//头像
      Container(),//右边部分
    ],
    )
    

    设置圆形图片

    CircleAvatar(
              backgroundImage: new AssetImage('images/1.jpeg'),
              radius: 100.0,
            )
    

    4.设备的宽高获取

    width: MediaQuery.of(context).size.width,
    height: MediaQuery.of(context).size.height,
    

    5.文字居中方向设置

    //Container属性
    alignment: Alignment.centerLeft,
    

    6.网络图片和本地图片的加载

    Container(
      width: 34,
      height: 34,
      margin: EdgeInsets.all(10),
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(6.0),
          image: DecorationImage(
            image: imageUrl != null
                ? NetworkImage(imageUrl) //网络图片
                : AssetImage(imageAssets),//本地图片
          )),
    )
    

    7.链式编程-添加数据

     @override
     //数据、对象创建
      void initState() {
        super.initState();
        //链式编程,调用两次addAllData并返回数组到 _listDatas
        _listDatas..addAll(datas)..addAll(datas);
        //数据排序
        _listDatas.sort((Friends a, Friends b){
          return a.indexLetter.compareTo(b.indexLetter);
        });
        //print('_listDatas:$_listDatas');
      }
    

    8.相除取整

    ~/

    onVerticalDragUpdate: (DragUpdateDetails details){
      print(details.globalPosition.dy);//相对于整个屏幕的值
      RenderBox box = context.findRenderObject();
      //计算当前位置 坐标转换, 算出y值
      double y = box.globalToLocal(details.globalPosition).dy;
      //y值除以每个item的高度就是当前的索引
      //每一个item的高度
      var itemH = ScreenHeight(context)/2/INDEX_WORDS.length;
      int index = y ~/ itemH;//相除取整
      print(box.globalToLocal(details.globalPosition));
    },
    

    9.数组越界处理

    //使用clamp
    //取值范围0~INDEX_WORDS.length-1 添加安全判断 
    int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length - 1);
    

    10.定义回调函数和调用

    //定义回调函数
    final void Function(String str) indexBarCallBack;
    //构造方法
    const IndexBar({Key key, this.indexBarCallBack}) : super(key: key);
    //调用该callBack
    //监听所在位置:计算当前位置
    onVerticalDragUpdate: (DragUpdateDetails details){
      widget.indexBarCallBack(getIndex(context, details.globalPosition));
    },
    //外部使用
    IndexBar(
        indexBarCallBack: (String str){
          print("收到了:$str");
        },
      ),
    

    11.PopupMenuButton 使用

    Container(
        margin: EdgeInsets.only(right: 10),
        child: PopupMenuButton(
          offset: Offset(0, 60.0),
          child: Image(image: AssetImage('images/圆加.png'),width: 25,),
          itemBuilder: _buildPopupMenuItem,
        ),
      )
        //创建Item的方法!
      PopupMenuItem<String> _buildItem(String imgAss, String title) {
        return PopupMenuItem(
          child: Row(
            children: <Widget>[
              Image(
                image: AssetImage(imgAss),
                width: 20,
              ),
              Container(
                width: 20,
              ),
              Text(
                title,
                style: TextStyle(color: Colors.white),
              ),
            ],
          ),
        );
      }
    
    //回调方法
      List<PopupMenuItem<String>> _buildPopupMenuItem(BuildContext context) {
        return <PopupMenuItem<String>>[
          _buildItem('images/发起群聊.png', '发起群聊'),
          _buildItem('images/添加朋友.png', '添加朋友'),
          _buildItem('images/扫一扫1.png', '扫一扫'),
          _buildItem('images/收付款.png', '收付款'),
        ];
      }
    

    设置popup背景颜色

    //MaterialApp -> theme -> cardColor
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',//针对Android 里面可用
      theme: ThemeData(
        primarySwatch: Colors.yellow,//主题色设置,深色:则时间和电池颜色为白色;浅色:则时间和电池颜色为黑色
        highlightColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
        splashColor: Color.fromRGBO(1, 0, 0, 0.0),//去掉tabBar默认选中效果
        cardColor: Color.fromRGBO(1, 1, 1, 0.65),//设置popup背景颜色
      ),
      home: RootPage(),
    )
    

    效果图:

    PopupMenuButton

    12.滑动ListView让键盘消失

    FocusScope.of(context).requestFocus(FocusNode());

    //监听ListView的滑动事件,让键盘消失
    Expanded(
        flex: 1, //占据剩余空间
        child: MediaQuery.removePadding(
          context: context,
          removeTop: true,
          child: NotificationListener(
            onNotification: (ScrollNotification note){
              FocusScope.of(context).requestFocus(FocusNode());
            },//滑动让键盘消失
            child: ListView.builder(
              itemCount: _models.length,
              itemBuilder: _itemForRow,
            ),
          ),
        ),
      )
    

    13.Containter 设置部分圆角和阴影效果

     @override
      Widget build(BuildContext context) {
        return Container(
          padding: EdgeInsets.only(top: 10, bottom: 10),
          decoration: BoxDecoration(
            color: Colors.white,
            // 设置阴影 要在裁剪之外添加一个Containter里面处理,否则无效
            boxShadow: [
              BoxShadow(
                  color: Color(0xff333333).withOpacity(0.05),
                  offset: Offset(0, 1.0),
                  blurRadius: 5),
            ],
          ),
          child: new ClipRRect(
            // 设置局部圆角
            borderRadius: BorderRadius.only(
              bottomLeft: Radius.circular(5),
              bottomRight: Radius.circular(5),
            ),
            child: Container(
              height: ScreenUtil().setHeight(147),
              child: Container(
                margin: EdgeInsets.only(left: 40, right: 40),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: navigatorList.map((item) {
                    return _navigatorItem(context, item);
                  }).toList(),
                ),
              ),
            ),
          ),
        );
      }
    

    14. Scaffold 的 appBar 去掉底部阴影

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        centerTitle: true,
        title: Text(
          "发现",
          style: TextStyle(
              color: Color(0xff333333), fontSize: ScreenUtil().setSp(34)),
        ),
        bottomOpacity: 0,
        elevation: 0, // 去掉底部阴影
      ),
    );
    

    其他

    @override
      Widget build(BuildContext context) {
        return Scaffold(
          //头部元素 比如:左侧返回按钮 中间标题 右侧菜单
          appBar: AppBar(
            title: Text('Scaffold脚手架组件示例'),
          ),
          //视图内容部分 通常作为应用页面的主显示区域
          body: Center(
            child: Text('Scaffold'),
          ),
          //底部导航栏
          bottomNavigationBar: BottomAppBar(
            child: Container(height: 50.0,),
          ),
          //添加FAB按钮
          floatingActionButton: FloatingActionButton(
            onPressed: () {},
            tooltip: '增加',
            child: Icon(Icons.add),
          ),
          //FAB按钮居中展示
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        );
    

    15.Image图片设置宽度自适应

    Container(
        margin: EdgeInsets.only(left: 15, right: 15),
        height: ScreenUtil().setHeight(243),
        decoration: BoxDecoration(
            image: DecorationImage(
          image: NetworkImage(
              'http://fdfs.xmcdn.com/group63/M0A/99/95/wKgMaFz_RD-BDyFjAAIO0iRtj0U176.jpg'),
          fit: BoxFit.fitWidth,
          alignment: Alignment.topCenter,
        )),
      ),
    

    16.FlutterListView嵌套GridView滚动冲突问题

    ListView和GirdView都是滚动Widget 两个部件嵌套就会存在滚动冲突,解决办法如下

    body: new ListView(
              shrinkWrap: true,
              padding: EdgeInsets.all(0),
              children: <Widget>[
                new GridView.count(
                 padding: EdgeInsets.all(0),
                physics: new NeverScrollableScrollPhysics(),//增加
                shrinkWrap: true,//增加
                crossAxisCount: 3,
                children:<Widget>[]
              ],
            ),
    
    ① 处理listview嵌套报错
    shrinkWrap: true,
    
    ②处理GridView中滑动父级Listview无法滑动
    physics: new NeverScrollableScrollPhysics();
    

    17.Flutter 自定义TabBar和修改indiactor 宽度

    1. 关键代码

    import 'package:flutter/material.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    
    class FriendsList extends StatefulWidget {
      @override
      _FriendsListState createState() => _FriendsListState();
    }
    class _FriendsListState extends State<FriendsList>
        with SingleTickerProviderStateMixin {
      TabController _tabController;
    
      @override
      void initState() {
        super.initState();
        _tabController = TabController(initialIndex: 0, length: 2, vsync: this);
      }
    
      @override
      void dispose() {
        super.dispose();
        _tabController.dispose();
      }
      
      @override
      Widget build(BuildContext context) {
        return DefaultTabController(
          length: 2,
          child: Scaffold(
            appBar: AppBar(
              title: Container(
                  height: ScreenUtil().setHeight(73),
                  alignment: Alignment.topLeft,
                  child: TabBar(
                    tabs: [
                      Tab(text: '好友'),
                      Tab(text: '心动'),
                    ],
                    controller: _tabController,
                    indicatorWeight: 2,
                    indicatorPadding: EdgeInsets.only(left: 10, right: 10),
                    labelPadding: EdgeInsets.symmetric(horizontal: 10),
                    isScrollable: true,
                    indicatorColor: Color(0xffFF7E98),
                    labelColor: Color(0xffFF7E98),
                    labelStyle: TextStyle(
                      fontSize: ScreenUtil().setSp(36),
                      color: Color(0xffFF7E98),
                      fontWeight: FontWeight.w500,
                    ),
                    unselectedLabelColor: Color(0xffAAAAAA),
                    unselectedLabelStyle: TextStyle(
                        fontSize: ScreenUtil().setSp(32), color: Color(0xffAAAAAA)),
                    indicatorSize: TabBarIndicatorSize.label,
                  )),
              backgroundColor: Colors.white,
              elevation: 0,
            ),
            body: TabBarView(
              children: [
                Container(
                  child: Center(
                    child: Text("好友页面"),
                  ),
                ),
                Container(
                  child: Center(
                    child: Text("心动页面"),
                  ),
                ),
              ],
              controller: _tabController,
            ),
          ),
        );
      }
    }
    

    2. 效果图

    效果图.gif

    18 fluro 插件 实现appBar不要出现返回键

    Application.router.navigateTo(context, "/index",replace: true);

    19.文字溢出处理

    ①Expanded + TextOverflow.ellipsis 设置省略号

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("LayoutPage")),
          body: Center(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Icon(
                  Icons.star,
                  size: 16.0,
                  color: Colors.grey,
                ),
                Padding(padding: new EdgeInsets.only(left: 5.0)),
                Expanded(
                  child: Text(
                    "100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000",
                    style: new TextStyle(color: Colors.grey, fontSize: 14.0),
                    // 设置省略号
                    overflow: TextOverflow.ellipsis,
                    // 设置最大行数
                    maxLines: 1,
                  ),
                )
              ],
            ),
          ),
        );
      }
    

    ② Expanded + TextOverflow.ellipsis 不生效

    通过

    • 限定Container 宽度
    • Row 布局嵌套 Expanded 可以添加约束
    TextOverflow.ellipsis 不生效

    20.数据解析报错之关键字 do

    do 为关键字,不能设置为Model的属性,应该用其他名称替换

    class UserChatList {
      int doType;
      UserChatList({this.doType});
      UserChatList.fromJson(Map<String, dynamic> json) {
        doType = json['do'];
      }
    

    21.聊天消息UI搭建

    效果图

    聊天消息U

    思路

    • ①Positison 设置
    right: ScreenUtil().setWidth(20),
    bottom: -ScreenUtil().setHeight(50),
    
    • ② Stack 设置溢出显示
    overflow: Overflow.visible
    

    关键代码

    /// 聊天Widget
      Widget ChatWidget(String chatType, String msg) {
        // 1 发出者
        if (chatType == 'send') {
          return Container(
            decoration: BoxDecoration(
              color: Colors.white,
              // 设置阴影
              boxShadow: [
                BoxShadow(
                  color: Color(0xffFF7E98),
                  offset: Offset(0, 1),
                  blurRadius: 8,
                )
              ],
              borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(15),
                  topRight: Radius.circular(15),
                  bottomRight: Radius.circular(15)),
            ),
            height: ScreenUtil().setHeight(80),
            margin: EdgeInsets.only(
              top: ScreenUtil().setHeight(40),
            ),
            padding: EdgeInsets.only(
                left: ScreenUtil().setWidth(40),
                right: ScreenUtil().setWidth(40),
                top: ScreenUtil().setHeight(10),
                bottom: ScreenUtil().setHeight(10)),
            child: Text(
              msg != null && msg.length > 0 ? msg : '',
              style: TextStyle(
                  fontSize: ScreenUtil().setSp(30), color: Color(0xff333333)),
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
            ),
          );
        } else if (chatType == 'minisend') {
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Stack(
                overflow: Overflow.visible,
                children: <Widget>[
                  // 消息
                  Container(
                    decoration: BoxDecoration(
                      color: Colors.white,
                      // 设置阴影
                      boxShadow: [
                        BoxShadow(
                          color: Color(0xffFF7E98),
                          offset: Offset(1, 1),
                          blurRadius: 8,
                        )
                      ],
                      borderRadius: BorderRadius.only(
                          topLeft: Radius.circular(15),
                          topRight: Radius.circular(15),
                          bottomRight: Radius.circular(15)),
                    ),
                    height: ScreenUtil().setHeight(80),
                    margin: EdgeInsets.only(
                      top: ScreenUtil().setHeight(40),
                    ),
                    padding: EdgeInsets.only(
                        left: ScreenUtil().setWidth(40),
                        right: ScreenUtil().setWidth(40),
                        top: ScreenUtil().setHeight(10),
                        bottom: ScreenUtil().setHeight(10)),
                    child: Text(
                      '😊很想认识你😊',
                      style: TextStyle(
                          fontSize: ScreenUtil().setSp(30),
                          color: Color(0xff333333)),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                  Positioned(
                    right: ScreenUtil().setWidth(20),
                    bottom: -ScreenUtil().setHeight(50),
                    child: // 小程序路径
                        Container(
                      margin: EdgeInsets.only(top: ScreenUtil().setHeight(16)),
                      child: Row(
                        children: <Widget>[
                          Text(
                            '去小程序查看',
                            style: TextStyle(
                                fontSize: ScreenUtil().setSp(22),
                                color: Color(0xffFF7E98)),
                          ),
                          Icon(
                            MyIcons.sex_boy,
                            size: ScreenUtil().setSp(16),
                            color: Color(0xffFF7E98),
                          )
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            ],
          );
        } else if (chatType == 'mine') {
          return Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              Container(
                decoration: BoxDecoration(
                  color: Colors.white,
                  // 设置阴影
                  boxShadow: [
                    BoxShadow(
                      color: Color(0xffFF7E98),
                      offset: Offset(0, 1),
                      blurRadius: 8,
                    )
                  ],
                  // 设置渐变色
                  gradient: LinearGradient(
                    colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
                    begin: Alignment(-1, -1),
                    end: Alignment(1.0, 0.56),
                  ),
                  // 设置圆角
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(15),
                      topRight: Radius.circular(15),
                      bottomLeft: Radius.circular(15)),
                ),
                height: ScreenUtil().setHeight(80),
                margin: EdgeInsets.only(
                  top: ScreenUtil().setHeight(40),
                ),
                padding: EdgeInsets.only(
                    left: ScreenUtil().setWidth(40),
                    right: ScreenUtil().setWidth(40),
                    top: ScreenUtil().setHeight(10),
                    bottom: ScreenUtil().setHeight(10)),
                child: Text(
                  msg != null && msg.length > 0 ? msg : '',
                  style: TextStyle(
                      fontSize: ScreenUtil().setSp(30), color: Colors.white),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              )
            ],
          );
        } else {
          return Container(
            margin: EdgeInsets.only(bottom:ScreenUtil().setHeight(40) ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                Stack(
                  overflow: Overflow.visible,
                  children: <Widget>[
                    Container(
                      decoration: BoxDecoration(
                        color: Colors.white,
                        // 设置阴影
                        boxShadow: [
                          BoxShadow(
                            color: Color(0xffFF7E98),
                            offset: Offset(0, 1),
                            blurRadius: 8,
                          )
                        ],
                        // 设置渐变色
                        gradient: LinearGradient(
                          colors: [Color(0xFFFF7E98), Color(0xFFFD7BAB)],
                          begin: Alignment(-1, -1),
                          end: Alignment(1.0, 0.56),
                        ),
                        // 设置圆角
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(15),
                            topRight: Radius.circular(15),
                            bottomLeft: Radius.circular(15)),
                      ),
                      height: ScreenUtil().setHeight(80),
                      margin: EdgeInsets.only(
                        top: ScreenUtil().setHeight(40),
                      ),
                      padding: EdgeInsets.only(
                          left: ScreenUtil().setWidth(40),
                          right: ScreenUtil().setWidth(40),
                          top: ScreenUtil().setHeight(10),
                          bottom: ScreenUtil().setHeight(10)),
                      child: Text(
                        msg != null && msg.length > 0 ? msg : '',
                        style: TextStyle(
                            fontSize: ScreenUtil().setSp(30), color: Colors.white),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ),
                    // 小程序路径
                    Positioned(
                      left: ScreenUtil().setWidth(20),
                      bottom: -ScreenUtil().setHeight(50),
                      child: Container(
                        margin: EdgeInsets.only(top: ScreenUtil().setHeight(16),),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.end,
                          children: <Widget>[
                            Text(
                              '去小程序查看',
                              style: TextStyle(
                                  fontSize: ScreenUtil().setSp(22),
                                  color: Color(0xffFF7E98)),
                            ),
                            Icon(
                              MyIcons.sex_boy,
                              size: ScreenUtil().setSp(16),
                              color: Color(0xffFF7E98),
                            )
                          ],
                        ),
                      ),
                    ),
                  ],
                )
              ],
            ),
          );
        }
      }
    

    22. Flutter 复制到剪切板

    通过Clipboard实现复制操作

    1.声明key并在Scaffold指定key

    /// 剪切板Key
    final clicpBoardKey  = new GlobalKey<ScaffoldState>();
    return Scaffold(
    key: clicpBoardKey,
    );
    

    2.实现复制操作并弹出SnackBar

    Clipboard.setData(ClipboardData(text: '人生若只初相见'));
    clicpBoardKey.currentState.showSnackBar(SnackBar(content: Text('已复制到剪贴板')));
    

    其他

    Scaffold.of(context).showSnackBar(SnackBar(
                    //提示信息内容部分
                    content: Text("显示SnackBar"),
                  ));
    

    23.Url 转义 decode

    decodeURIComponent('%2Fpage%2Forigin%2Forigin%3Fuid%3D')
    

    24.获取widget 控件的尺寸

    • 通过Context
    • 通过GlobalKey
    • 通过LayoutChangedNotification(重写)
    // 宽度
    width: MediaQuery.of(context).size.width,
    // 高度
    height: MediaQuery.of(context).size.height * 0.05,
    // 注意: context 为父组件的context
    
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    class NewSizeChangedLayoutNotification extends LayoutChangedNotification{
      Size size;
    
      NewSizeChangedLayoutNotification(this.size);
    }
    
    class NewSizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
      const NewSizeChangedLayoutNotifier({
        Key key,
        Widget child,
      }) : super(key: key, child: child);
    
      @override
      _NewRenderSizeChangedWithCallback createRenderObject(BuildContext context) {
        return _NewRenderSizeChangedWithCallback(
            onLayoutChangedCallback: (Size size) {
              NewSizeChangedLayoutNotification(size).dispatch(context);
            }
        );
      }
    }
    
    typedef VoidCallbackWithParam = Function(Size size);
    
    class _NewRenderSizeChangedWithCallback extends RenderProxyBox {
      _NewRenderSizeChangedWithCallback({
        RenderBox child,
        @required this.onLayoutChangedCallback,
      }) : assert(onLayoutChangedCallback != null),
            super(child);
    
      final VoidCallbackWithParam onLayoutChangedCallback;
    
      Size _oldSize;
    
      @override
      void performLayout() {
        super.performLayout();
        //在第一次layout结束后就会进行通知
        if (size != _oldSize)
          onLayoutChangedCallback(size);
        _oldSize = size;
      }
    }
    

    25.decoration相关

    1) 边框

    // 同时设置4条边框:1px粗细的黑色实线边框
    BoxDecoration(
      border: Border.all(color: Colors.black, width: 1, style: BorderStyle.solid)
    )
    
    // 设置单边框:上边框为1px粗细的黑色实线边框,右边框为1px粗细的红色实线边框
    BoxDecoration(
      border: Border(
        top: BorderSide(color: Colors.black, width: 1, style: BorderStyle.solid),
        right: BorderSide(color: Colors.red, width: 1, style: BorderStyle.solid),
      ),
    )
    

    2) 圆角

    // 同时设置4个角的圆角为5
    BoxDecoration(
      borderRadius: BorderRadius.circular(5),
    )
    
    // 设置单圆角:左上角的圆角为5,右上角的圆角为10
    BoxDecoration(
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(5),
        topRight: Radius.circular(10),
      ),
    )
    

    3) 阴影

    BoxDecoration(
      boxShadow: [
        BoxShadow(
          offset: Offset(0, 0),
          blurRadius: 6,
          spreadRadius: 10,
          color: Color.fromARGB(20, 0, 0, 0),
        ),
      ],
    )
    

    4) 渐变色

    // 从左到右,红色到蓝色的线性渐变
    BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.centerLeft,
        end: Alignment.centerRight,
        colors: [Colors.red, Colors.blue],
      ),
    )
    
    // 从中心向四周扩散,红色到蓝色的径向渐变
    BoxDecoration(
      gradient: RadialGradient(
        center: Alignment.center,
        colors: [Colors.red, Colors.blue],
      ),
    )
    // 设置角度
    final gradient = Utils.parseAngleToAlignment(90);
    BoxDecoration(
        gradient: LinearGradient(
            colors: [
                Color(0xFFFFA3AD),
                Color(0xFFFC5E72)
            ],
            begin: Alignment(gradient['beginX'], gradient['beginY']),
            end: Alignment(gradient['endX'], gradient['endY'])
        ),
        borderRadius: BorderRadius.circular(2)
    )
    

    26.MaterialApp 使用讲解

    
    字段  类型
    
    home(主页)    Widget
    routes(路由)  Map<String, WidgetBuilder>
    theme(主题)   ThemeData
    debugShowMaterialGrid(调试显示材质网格) bool
    
    navigatorKey(导航键)   GlobalKey<NavigatorState>
    onGenerateRoute(生成路由)   RouteFactory
    onUnknownRoute(未知路由)    RouteFactory
    navigatorObservers(导航观察器)   List<NavigatorObserver>
    initialRoute(初始路由)  String
    builder(建造者)    TransitionBuilder
    title(标题)   String
    onGenerateTitle(生成标题)   GenerateAppTitle
    color(颜色)   Color
    locale(地点)  Locale
    localizationsDelegates(本地化委托)   Iterable<LocalizationsDelegate<dynamic>>
    localeResolutionCallback(区域分辨回调)    LocaleResolutionCallback
    supportedLocales(支持区域)  Iterable<Locale>
    showPerformanceOverlay(显示性能叠加)  bool
    checkerboardRasterCacheImages(棋盘格光栅缓存图像)    bool
    checkerboardOffscreenLayers(棋盘格层)   bool
    showSemanticsDebugger(显示语义调试器)  bool
    debugShowCheckedModeBanner(调试显示检查模式横幅)  bool
    

    27.使用FutureBuilder每调用一次setState就会重新请求future

    解决方法:将 future提取出来,作为一个变量

    Future<int> future;
    
      @override
      void initState() {
        super.initState();
        future=getInt();
      }
    
      FutureBuilder<int>(
        future: future,
        builder: (context, snapshot) {
          return ...;
        }
      ),
    
      Future<int> getInt(){
        return Future.value(1);
      }
    

    28.输入框内容为空时,长按不显示粘贴工具栏

    将输入框中的autoFocus属性为ture去掉

    29.Flutter 左上角返回按钮回调(CallBack)

    1.1 async await 实现

    /// 跳转到下级页面时 await Navigator.pushNamed
    onTap: () async {
        await Navigator.pushNamed(context, '/account');
        //执行 刷新数据操作
        refrshData();
      },
    

    2.嵌套封装 会导致await 失效

    class NavigatorUtil{
      /// 通用跳转
      static push(BuildContext context,Widget widget ) {
        Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300),
            pageBuilder: (context, animation, secondaryAnimation){
              return new FadeTransition( //使用渐隐渐入过渡,
                opacity: animation,
                child:widget,
              );
            })
        );
      }
    }
    
    //使用导致await失效
    onTap: () async {
        // 其他
         await NavigatorUtil.push(context, widget);
         //执行刷新操作
      },
    

    解决方案
    封装层嵌套 async await

    class NavigatorUtil{
      /// 通用跳转
      static push(BuildContext context,Widget widget ) async {
        await Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300),
            pageBuilder: (context, animation, secondaryAnimation){
              return new FadeTransition( //使用渐隐渐入过渡,
                opacity: animation,
                child:widget,
              );
            })
        );
      }
    }
    
    

    30.GestureDetector 手势冲突

    解决手势冲突 - IgnorePointer

    IgnorePointer(
      child: GestureDetector(
        child: Container(
          height: ScreenUtil().setHeight(300),
          width: Screen.width,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(ScreenUtil().setWidth(20)),
              topRight: Radius.circular(ScreenUtil().setWidth(20)),
            ),
            gradient: LinearGradient(
              colors: [
                Color(0xFFFFFFFF),
                Colors.white.withOpacity(.2),
                Colors.white.withOpacity(0),
                Colors.white.withOpacity(0),
                Colors.white.withOpacity(0)
              ],
              begin: Alignment(
                  topGradient['beginX'], topGradient['beginY']),
              end: Alignment(topGradient['endX'], topGradient['endY']),
            ),
          ),
        ),
        onTap: () {
          backToTop();
        },
      ),
    ),
    

    31.TextField 设置border 颜色(黑线修改颜色)

    /// 输入框
    Container(
        child: Theme(
            data: ThemeData(
                    primaryColor: Colors.white, hintColor: Colors.white),
            child: TextField(
                style: TextStyle(
                    fontSize: ScreenUtil().setSp(36),
                    color: Colors.white,
                ),
                controller: inputController,
                onChanged: handlePhoneInput,
                autofocus: true,
                decoration: new InputDecoration(
                    border: const UnderlineInputBorder(
                        borderSide: BorderSide(style: BorderStyle.solid,color: Colors.white,),
                    ),
                    contentPadding: EdgeInsets.only(
                        left: ScreenUtil().setWidth(100),
                        right: ScreenUtil().setWidth(20),
                        top: ScreenUtil().setWidth(20),
                        bottom: ScreenUtil().setWidth(20),
                    ),
                    hintText: '输入手机号',
                    hintStyle: TextStyle(
                        color: Color.fromRGBO(255, 255, 255, .7),
                        fontSize: ScreenUtil().setSp(36),
                    ),
                ),
            ),
        ),
    ),
    

    32.decoration 阴影设置无边界

    通过Opacity 以及 LinearGradient设置 stops节点和colors 结合

    // 顶部阴影
    Opacity(
      opacity: 0.23,
      child: Container(
        height: ScreenUtil().setHeight(129),
        decoration: BoxDecoration(
          gradient: LinearGradient(
              stops: [
                0,
                .8
              ],
              colors: [
                Color(0xff565656),
                Color(0xFF030303).withOpacity(0),
              ],
              begin:
                  Alignment(gradient['beginX'], gradient['beginY']),
              end: Alignment(gradient['endX'], gradient['endY'])),
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(10.0),
            topRight: Radius.circular(10.0),
          ),
        ),
        child: Container(
          margin: EdgeInsets.only(
            left: ScreenUtil().setWidth(10),
            right: ScreenUtil().setWidth(17),
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Container(
                margin: EdgeInsets.only(
                    top: ScreenUtil().setHeight(17)),
                child: Row(
                  children: <Widget>[
                    Icon(
                      MyIcons.heart,
                      size: ScreenUtil().setSp(40),
                      color: Colors.white,
                    ),
                    Container(
                      margin: EdgeInsets.only(
                        left: ScreenUtil().setWidth(5),
                        top: ScreenUtil().setWidth(5),
                      ),
                      child: Text(
                        this.widget.item != null &&
                                this.widget.item.praises != null
                            ? this.widget.item.praises.toString()
                            : '',
                        style: TextStyle(
                          fontSize: ScreenUtil().setSp(20),
                          color: Colors.white,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    )
    

    33.Dart List.asMap() 获取下标

     this.list.asMap().keys.map((i) {
       // i 为下标
        return _itemUI(context, i);
      }).toList()
    

    34.indexWhere 获取数组索引

    int currentIndex = this.renderList.indexWhere((item) => item.id == feed.id);
    

    35.build runner 插件使用

    build runner 插件编译生成属性快捷键

    flutter packages run build_runner build --delete-conflicting-outputs
    

    36.Container点击区域过小

    GestureDetector 内Container不设置color点击区域会根据内容大小来定

    37.xcrun instruments 打开模拟器

    xcrun instruments -w "iPhone 8 Plus (13.1)"

    39. GestureDetector处理手势操作 behavior 行为

    • HitTestBehavior.opaque 自己处理事件

    • HitTestBehavior.deferToChild child处理事件

    • HitTestBehavior.translucent 自己和child都可以接收事件

    40.Widget无法居中,对齐

     Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
              Container(
                  width: ScreenUtil().setHeight(114),
                  height: ScreenUtil().setHeight(114),
                  margin: EdgeInsets.only(
                    left: ScreenUtil().setWidth(10),
                  ),
                  child: Center(child: FailedDot(),),
                )
              : Container()
        ],
      ),
    

    41.Flutter Container 点击区域太小

    使用GestureDetector包裹Container,发现在Container内容为空的区域点击时,捕捉不到onTap点击事件。
    解决方案:在GestureDetector里面添加属性:behavior: HitTestBehavior.opaque,即可:

    GestureDetector(
              behavior: HitTestBehavior.opaque,
              child: Container( width: ScreenUtil().setHeight(114),
                  height: ScreenUtil().setHeight(114),child:Text('点我')),
              onTap: () {
                this.handlePlayVoice();
              },
            )
    

    42.监听页面返回事件(返回按钮点击+侧滑返回)

    侧滑不会触发onBack回调,因此使用WillPopScopeonWillPop来实现

     @override
      Widget build(BuildContext context) {
        return WillPopScope(
          onWillPop: () async {
            // 设置草稿箱
            this.setCraft();
            return true;
          },
          child: Container()
      }
    

    43PageView使用注意事项

    问题描述:第一次指定加载第二个page,切换时需要切换两次才显示正常

    原因分析
    PageView未初始化时默认index = 0,你强行修改时会导致两个index不一致

    解决办法

     _controller = PageController(initialPage: currentIndex);
    /// 切换
    _controller.animateToPage(
                                      currentIndex,
                                      duration: Duration(
                                        milliseconds:
                                            (pageSwitchAnimatedTime + 100),
                                      ),
                                      curve: Curves.ease,
                                    );
    

    44 ClipOval组件详解

    • 圆形裁剪(超出部分隐藏,相当于Stack的overFlow为clip)
    Center(
          child: ClipOval(
            child: Image.asset(
              "images/app.png",
              width: 100.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
        )
    

    45如何判断设备是否为iPad

    // 1.导入设备信息插件
    # 设备信息
    device_info: ^0.4.0
    
    // 2. 使用
    Future<bool> isIpad() async{
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      IosDeviceInfo info = await deviceInfo.iosInfo;
      if (info.name.toLowerCase().contains("ipad")) {
        return true;
      }
      return false;
    }
    

    46 setState导致的内存泄漏——setState() called after dispose()

    错误原因

    • 定时器没有被销毁(dispose 销毁cancel)

    解决方法

    临时方案

    • 异步消息未返回,所以在setState方法之前调用mouted属性进行判断即可
    if (mounted) {
      setState(() {
        //refreshData
      });
    }
    

    最终解决方案

    @override
      void dispose() {
        _countdownTimer?.cancel();
        super.dispose();
      }
    

    47.Flutter 设置圆角图片

    通过 ClipRRect + borderRadius 实现

    Container(
          child: ClipRRect(
            borderRadius: BorderRadius.circular(10),
            child: Image.network(
              this.matchedUser.user.avatar,
              fit: BoxFit.cover,
            ),
          ),
          width: AdaptationUtils.px(140),
          height: AdaptationUtils.px(140),
        )
    

    48.clamp 语法

    顾名思义,夹紧,所得的结果不会超出这个范围,类似于闭区间[]
    例如

    int origen = 10;
    int result = origen.clamp(2, 11);
    print(result);//MARK:结果为10
    //////////////////////////////////
    int origen = 10;
    int result = origen.clamp(2, 9);
    print(result);//MARK:结果为9
    

    49.Flutter 扩大点击区域(Container)

    效果图

    扩大点击区域.gif

    代码封装

    /// 扩大点击区域(Container)
    ///
    /// created by hujintao
    /// created at 2020-03-19
    //
    import 'package:flutter/material.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    
    class EnlargeWrapper extends StatefulWidget {
      Widget child;
      EdgeInsetsGeometry enlarge;
    
      EnlargeWrapper({
        this.child,
        this.enlarge,
      });
    
      @override
      _EnlargeWrapperState createState() => _EnlargeWrapperState();
    }
    
    class _EnlargeWrapperState extends State<EnlargeWrapper> {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: this.widget.child,
          padding: this.widget.enlarge ??
              EdgeInsets.fromLTRB(
                ScreenUtil().setWidth(40),
                ScreenUtil().setWidth(40),
                ScreenUtil().setWidth(40),
                ScreenUtil().setWidth(40),
              ),
    //      color: Colors.yellow,//测试
          color: Colors.transparent,
        );
      }
    }
    

    使用

    Center(
        child: GestureDetector(
          child: EnlargeWrapper(
            child: Container(
              width: 100,
              height: 100,
              color: Colors.red,
              child: Center(
                child: Text('点我s撒'),
              ),
            ),
            enlarge: EdgeInsets.all(50),
          ),
          onTap: () {
            Toast.show('点击了');
          },
        ),
      )
    

    50.根据名称读取文件

    //根据名称读取文件
    import 'dart:io';
    
    readFile(name) async {
      //创建文件对象
      var file = File(name);
      try {
        //判断是否存在
        bool exists = await file.exists();
        if (exists) {
          //如果存在
          print(await file.length()); //文件大小(字节)---137
          print(await file.lastModified()); //最后修改时间---2018-12-21 13:49:35.000
          print(file.parent.path); //获取父文件夹的路径---C:\Users\Administrator\Desktop\dart
          return await file.readAsString(); //读取文件并返回
        } else {
          await file.create(recursive: true); //不存在则创建文件
          return "未发现文件,已为您创建!Dart机器人:2333";
        }
      } catch (e) {
        //异常处理
        print(e);
      }
    }
    

    使用

    var result = await readFile("\Users\XXX\Desktop\dart\test.txt");
      print(result);
    

    添加所有SD卡文件名称

    // 添加所有SD卡文件名称
      localPath() {
        try {
          var perm =
              SimplePermissions.requestPermission(Permission.ReadExternalStorage);
          var sdPath = getExternalStorageDirectory();
          sdPath.then((file) {
            perm.then((v) {
              file.list().forEach((i) {
                _files.add(i.path);
              });
              setState(() {});
            });
          });
        } catch (err) {
          print(err);
        }
      }
    

    51 Flutter 微信分享和支付回调

    通过 fluwx 微信SDK插件
    下载插件

    # 微信sdk
    fluwx: ^1.2.1+1
    

    1.微信分享回调

    import 'package:fluwx/fluwx.dart' as fluwx;
    /// 微信分享监听
    StreamSubscription wxShareListener;
     @override
      void initState() {
        super.initState();
        this.track();
        initWxShareListener();
      }
      /// 初始化微信分享监听
      void initWxShareListener() {
        wxShareListener =
            fluwx.responseFromShare.listen(this.onWxShareResponse);
      }
      /// 微信分享响应
      void onWxShareResponse(fluwx.WeChatShareResponse response) {
        print(
            'pay success: ${response.errStr}, ${response.type}, ${response.errCode}, ${response.androidOpenId}');
        if (response.errCode == 0) {
         Toast.show('分享回来了额~~~~');
        } 
      }
      // 销毁监听
      @override
      void dispose() {
        wxShareListener.cancel();
        super.dispose();
      }
    

    2.微信支付回调

    import 'package:fluwx/fluwx.dart' as fluwx;
    /// 微信支付监听
    StreamSubscription fluwxPaymentListener;
     @override
      void initState() {
        super.initState();
        this.track();
        initWxPayListener();
      }
      /// 初始化微信支付监听
      void initWxPayListener() {
        fluwxPaymentListener =
            fluwx.responseFromPayment.listen(this.onWxPayResponse);
      }
      /// 微信支付响应
      void onWxPayResponse(fluwx.WeChatPaymentResponse response) {
        print(
            'pay success: ${response.errStr}, ${response.type}, ${response.errCode}, ${response.androidOpenId}');
    
        // 微信响应之后, 不允许关闭窗口
        if (this.canClose) {
          this.canClose = false;
          setState(() {});
        }
    
        if (response.errCode == 0) {
          this.checkOrderStatus();
        } else {
          if (response.errCode == -1) {
            this.onError(PayErrorEnum.WxPayError);
          } else if (response.errCode == -2) {
            this.onError(PayErrorEnum.WxPayCanceled);
          } else {
            this.onError(PayErrorEnum.WxPayUnKnowError);
          }
        }
      }
      // 销毁监听
      @override
      void dispose() {
        fluwxPaymentListener.cancel();
        super.dispose();
      }
    

    52.数据库操作抽象类(接口封装)

    //数据库操作抽象类
    abstract class DateBaseOperate {
    
      void insert(); //定义插入的方法
    
      void delete(); //定义删除的方法
    
      void update(); //定义更新的方法
    
      void query(); //定义一个查询的方法
    }
    
    //数据库操作实现类
    class DateBaseOperateImpl extends DateBaseOperate {
    
      //实现了插入的方法
      void insert(){
        print('实现了插入的方法');
      }
    
      //实现了删除的方法
      void delete(){
        print('实现了删除的方法');
      }
    
      //实现了更新的方法
      void update(){
        print('实现了更新的方法');
      }
    
      //实现了一个查询的方法
      void query(){
        print('实现了一个查询的方法');
      }
    
    }
    
    
    main() {
    
      var db = new DateBaseOperateImpl();
      db.insert();
      db.delete();
      db.update();
      db.query();
    
    }
    

    53 .继承

    //动物类
    class Animal {
    
      //动物会吃
      void eat(){
        print('动物会吃');
      }
    
      //动物会跑
      void run(){
        print('动物会跑');
      }
    }
    //人类
    class Human extends Animal {
    
      //人类会说
      void say(){
        print('人类会说');
      }
    
      //人类会学习
      void study(){
        print('人类会学习');
      }
    }
    
    main() {
      print('实例化一个动物类');
      var animal = new Animal();
      animal.eat();
      animal.run();
    
      print('实例化一个人类');
      var human = new Human();
      human.eat();
      human.run();
      human.say();
      human.study();
    }
    

    54.流程控制语句

    void test(){
    
        //if else 示例
    //    String today = 'Monday';
    //    if (today == 'Monday') {
    //      print('今天是星期一');
    //    } else if (today == 'Tuesday') {
    //      print('今天是星期二');
    //    } else {
    //      print('今天是个好日子');
    //    }
    
    
        //for循环示例
        var message = new StringBuffer("Hello Dart");
        for (var i = 0; i < 5; i++) {
          message.write('!');
        }
        print(message);
    
        //forEach示例
    //    var arr = [0, 1, 2, 3, 4, 5, 6];
    //    for (var v in arr) {
    //      print(v);
    //    }
    
        //while循环示例
    //    var _temp = 0;
    //    while(_temp < 5){
    //
    //      print("这是一个循环: " + (_temp).toString());
    //      _temp ++;
    //    }
    
        //do-while循环示例
    //    var _temp = 0;
    //
    //    do{
    //      print("这是一个循环: " + (_temp).toString());
    //      _temp ++;
    //    }
    //    while(_temp < 5);
    
        //break continue示例
        var arr = [0, 1, 2, 3, 4, 5, 6];
        for (var v in arr) {
          if(v == 2 ){
            //break;
            continue;
          }
          print(v);
        }
    
    
        //switch case示例
        String today = 'Monday';
        switch (today) {
          case 'Monday':
            print('星期一');
            break;
          case 'Tuesday':
            print('星期二');
            break;
        }
    
        //异常处理示例
        try {
          // ···
        } on Exception catch (e) {
          print('Exception details:\n $e');
        } catch (e, s) {
          print('Exception details:\n $e');
          print('Stack trace:\n $s');
        } finally {
          print('Do some thing:\n');
        }
      }
    

    54 .Getters 和 Setters 方法

    class Rectangle {
      num left;
      num top;
      num width;
      num height;
    
      Rectangle(this.left, this.top, this.width, this.height);
    
      //获取right值
      num get right         => left + width;
    
      //设置right值 同时left也发生变化
      set right(num value)  => left = value - width;
    
      //获取bottom值
      num get bottom        => top + height;
    
      //设置bottom值 同时top也发生变化
      set bottom(num value) => top = value - height;
    }
    

    55. Text设置下划线,虚线和删除线

    TextStyle(
                  //字体颜色
                  color: const Color(0xffff0000),
                  //文本装饰器(删除线)
                  decoration: TextDecoration.lineThrough,
                  //文本装饰器颜色(删除线颜色)
                  decorationColor: const Color(0xff000000),
                  //字体大小
                  fontSize: 18.0,
                  //字体样式 是否斜体
                  fontStyle: FontStyle.italic,
                  //字体粗细
                  fontWeight: FontWeight.bold,
                  //文字间距
                  letterSpacing: 2.0,
                )
    // 下划线
    color: const Color(0xffff9900),
    decoration: TextDecoration.underline,
    // 虚线
    decoration: TextDecoration.underline,
    decorationStyle: TextDecorationStyle.dashed,
    // 斜体
    fontStyle: FontStyle.italic,
    fontWeight: FontWeight.bold,
    

    56.PopupMenuButton组件示例

    //会控菜单项
    enum ConferenceItem { AddMember, LockConference, ModifyLayout, TurnoffAll }
    FlatButton(
        onPressed: () {},
        child: PopupMenuButton<ConferenceItem>(
          onSelected: (ConferenceItem result) {},
          itemBuilder: (BuildContext context) =>//菜单项构造器
          <PopupMenuEntry<ConferenceItem>>[
            const PopupMenuItem<ConferenceItem>(//菜单项
              value: ConferenceItem.AddMember,
              child: Text('添加成员'),
            ),
            const PopupMenuItem<ConferenceItem>(
              value: ConferenceItem.LockConference,
              child: Text('锁定会议'),
            ),
            const PopupMenuItem<ConferenceItem>(
              value: ConferenceItem.ModifyLayout,
              child: Text('修改布局'),
            ),
            const PopupMenuItem<ConferenceItem>(
              value: ConferenceItem.TurnoffAll,
              child: Text('挂断所有'),
            ),
          ],
        ),
      )
    

    57.TextField组件详解

    TextField(
          //绑定controller
          controller: controller,
          //最大长度,设置此项会让TextField右下角有一个输入数量的统计字符串
          maxLength: 30,
          //最大行数
          maxLines: 1,
          //是否自动更正
          autocorrect: true,
          //是否自动对焦
          autofocus: true,
          //是否是密码
          obscureText: false,
          //文本对齐方式
          textAlign: TextAlign.center,
          //输入文本的样式
          style: TextStyle(fontSize: 26.0, color: Colors.green),
          //文本内容改变时回调
          onChanged: (text) {
            print('文本内容改变时回调 $text');
          },
          //内容提交时回调
          onSubmitted: (text) {
            print('内容提交时回调 $text');
          },
          enabled: true, //是否禁用
          decoration: InputDecoration(//添加装饰效果
              fillColor: Colors.grey.shade200,//添加灰色填充色
              filled: true,
              helperText: '用户名',
              prefixIcon: Icon(Icons.person),//左侧图标
              suffixText: '用户名'),//右侧文本提示
        )
    

    58. 滑动删除示例

    效果图
    滑动删除.gif
    代码
    import 'package:flutter/material.dart';
    void main() {
      runApp(new MaterialApp(
        title: '滑动删除示例',
        home: new MyApp(),
      ));
    }
    
    class MyApp extends StatelessWidget {
    
      //构建30条列表数据
      List<String> items = new List<String>.generate(30, (i) => "列表项 ${i + 1}");
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('滑动删除示例'),
          ),
          //构建列表
          body: new ListView.builder(
            itemCount: items.length,//指定列表长度
            itemBuilder: (context, index) {//构建列表
    
              //提取出被删除的项
              final item = items[index];
    
              //返回一个可以被删除的列表项
              return new Dismissible(
                  key: new Key(item),
                  //被删除回调
                  onDismissed: (direction) {
                    //移除指定索引项
                    items.removeAt(index);
                    //底部弹出消息提示当前项被删除了
                    Scaffold.of(context).showSnackBar(
                        new SnackBar(content: new Text("$item 被删除了")));
                  },
                  child: new ListTile(title: new Text('$item'),)
              );
            },
          ),
        );
      }
    }
    

    59 自定义字体添加及使用

    • 1.放置字体资源


      放置字体资源
    • 2.在pubspec.yaml配置路径

    fonts:
        - family: myfont
          fonts:
            - asset: fonts/myfont.ttf
    
    • 3.使用
    Center(
        child: new Text(
          '你好 flutter',
          style: new TextStyle(fontFamily: 'myfont',fontSize: 36.0),
        ),
      )
    
    • 效果图
    效果图

    60.页面跳转返回数据

    //压栈操作并等待页面返回操作
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => SecondPage()),
    );
    //读取并显示返回值
    Scaffold.of(context).showSnackBar(SnackBar(content: Text("$result")));
    
    //出栈带上参数 返回到上一个页面
    Navigator.pop(context, 'hi flutter');
    

    61.fluro 企业级路由使用

    • 导入依赖
    fluro: ^1.5.0
    
    • 组件封装
    // 1.定义Application类
    import 'package:fluro/fluro.dart';
    
    //定义Application类
    class Application{
      //使用静态变量创建Router对象
      static Router router;
    }
    
    // 2.定义路由集和handler
    /// 2.1 handler
    import 'package:fluro/fluro.dart';
    import '../pages/second_page.dart';
    import 'package:flutter/material.dart';
    
    //创建Handler用来接收路由参数及返回第二个页面对象
    Handler secondPageHandler = Handler(
      handlerFunc: (BuildContext context,Map<String,List<String>> params){
        //读取goodId参数 first即为第一个数据
        String goodId = params['goodId'].first;
        return SecondPage(goodId);
      }
    );
    
    /// 2.2 路由集 
    import 'package:fluro/fluro.dart';
    import 'package:flutter/material.dart';
    import 'router_handler.dart';
    
    
    //路由集
    class Routes{
      //根路径
      static String root = '/';
      //第二个页面路径
      static String secondPage = '/secondPage';
    
      //配置路由对象
      static void configureRoutes(Router router){
    
        //没有找到路由的回调方法
        router.notFoundHandler = Handler(
          handlerFunc: (BuildContext context,Map<String,List<String>> params){
            print('error::: router 没有找到');
          }
        );
    
        //定义第二页面路由的Handler
        router.define(secondPage, handler: secondPageHandler);
    
      }
    
    }
    
    
    • 第一个页面
    import 'package:flutter/material.dart';
    import '../routers/application.dart';
    import 'package:fluro/fluro.dart';
    
    
    //第一个页面
    class FirstPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Fluro路由导航示例"),
          ),
          body: Center(
            child: RaisedButton(
              //点击处理
              onPressed: () {
                _navigateToSecondPage(context);
              },
              child: Text('打开第二个页面'),
            ),
          ),
        );
      }
    
      //路由至第二个页面
      _navigateToSecondPage(BuildContext context) async {
        //路由带的参数
        String goodId  = '001';
        //通过Application类里的路由router导航至第二个页面 可指定页面切换动画类型
        Application.router.navigateTo(context, "/secondPage?goodId=$goodId",transition: TransitionType.fadeIn).then((result) {//回传值
          //当第二个页面返回时带的参数为result值
          if (result != null) {
            print(result);
          }
        });
      }
    }
    
    • 第二个页面
    import 'package:flutter/material.dart';
    
    //第二个页面
    class SecondPage extends StatelessWidget {
      //传递参数值
      final String goodId;
    
      SecondPage(this.goodId);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("第二个页面"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                //显示传递参数值
                Text(
                  '$goodId',
                  style: TextStyle(
                    fontSize: 28.0,
                    color: Colors.red,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: RaisedButton(
                    onPressed: () {
                      //出栈带上参数 返回至第一个页面
                      Navigator.pop(context, '第二个页面返回参数($goodId)');
                    },
                    child: Text('点击返回'),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    • 配置路由
    import 'package:flutter/material.dart';
    import './routers/routes.dart';
    import 'package:fluro/fluro.dart';
    import './routers/application.dart';
    import './pages/first_page.dart';
    
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
    
        //创建路由对象
        final router = Router();
        //配置路由集Routes的路由对象
        Routes.configureRoutes(router);
        //指定Application的路由对象
        Application.router = router;
    
        return Container(
          child: MaterialApp(
            title: "Fluro路由导航示例",
            debugShowCheckedModeBanner: false,
            //生成路由的回调函数,当导航的命名路由的时候,会使用这个来生成界面
            onGenerateRoute: Application.router.generator,
            //主页指定为第一个页面
            home: FirstPage(),
          ),
        );
      }
    }
    

    62.数据库操作('Sqlite')

    模型

    //客户数据模型类
    class Client {
      //id
      int id;
      //姓名
      String name;
      //年龄
      int age;
      //性别
      bool sex;
    
      Client({this.id, this.name, this.age, this.sex,});
    
      //将JSON数据转换成数据模型
      factory Client.fromMap(Map<String, dynamic> json) => Client(
            id: json["id"],
            name: json["name"],
            age: json["age"],
            sex: json["sex"] == 1,
          );
    
      //将数据模型转换成JSON
      Map<String, dynamic> toMap() => {
            "id": id,
            "name": name,
            "age": age,
            "sex": sex,
          };
    }
    

    组件封装

    import 'dart:async';
    import 'dart:io';
    import 'package:path/path.dart';
    import 'package:path_provider/path_provider.dart';
    import 'client.dart';
    import 'package:sqflite/sqflite.dart';
    
    //数据库操作封装类
    class DBProvider {
      DBProvider._();
    
      static final DBProvider db = DBProvider._();
    
      Database _database;
    
      //获取Database对象
      Future<Database> get database async {
        //使用单例模式创建Database对象
        if (_database != null) {
          return _database;
        }
        _database = await initDB();
        return _database;
      }
    
      //初始化数据库
      initDB() async {
        //获取文档目录对象
        Directory documentsDirectory = await getApplicationDocumentsDirectory();
        //获取默认数据库位置(在Android上,它通常是data/data/<package_name>/databases,在iOS上,它是Documents目录)
        String path = join(documentsDirectory.path, "client.db");
        //打开数据库 传入路径 版本号 打开完成回调函数
        return await openDatabase(path, version: 1, onOpen: (db) {},
            onCreate: (Database db, int version) async {
              //数据库创建完成后创建Client表
              await db.execute("CREATE TABLE Client ("
                  "id INTEGER PRIMARY KEY,"
                  "name TEXT,"
                  "age INTEGER,"
                  "sex BIT"
                  ")");
        });
      }
    
      //新增Client
      insertClient(Client newClient) async {
        final db = await database;
        //获取表中最大的id再加1作为新的id
        var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Client");
        int id = table.first["id"];
        //向表中插入一条数据
        var raw = await db.rawInsert(
            "INSERT Into Client (id,name,age,sex)"
            " VALUES (?,?,?,?)",
            [id, newClient.name, newClient.age, newClient.sex]);
        return raw;
      }
    
      //修改性别
      updateSex(Client client) async {
        final db = await database;
        Client newClient = Client(
            id: client.id,
            name: client.name,
            age: client.age,
            sex: !client.sex);
        //更新当前Client的性别
        var res = await db.update("Client", newClient.toMap(),
            where: "id = ?", whereArgs: [client.id]);
        return res;
      }
    
      //更新Client
      updateClient(Client newClient) async {
        final db = await database;
        var res = await db.update("Client", newClient.toMap(),
            where: "id = ?", whereArgs: [newClient.id]);
        return res;
      }
    
      //根据id获取Client
      getClient(int id) async {
        final db = await database;
        //根据id查询表记录
        var res = await db.query("Client", where: "id = ?", whereArgs: [id]);
        //将查询返回的数据转换为Client对象并返回
        return res.isNotEmpty ? Client.fromMap(res.first) : null;
      }
    
      //获取所有Client
      Future<List<Client>> getAllClients() async {
        final db = await database;
        var res = await db.query("Client");
        List<Client> list = res.isNotEmpty ? res.map((c) => Client.fromMap(c)).toList() : [];
        return list;
      }
    
      //根据id删除Client
      deleteClient(int id) async {
        final db = await database;
        return db.delete("Client", where: "id = ?", whereArgs: [id]);
      }
    
      //删除所有Client
      deleteAll() async {
        final db = await database;
        db.rawDelete("Delete * from Client");
      }
    }
    

    组件使用

    // 获取所有数据
    DBProvider.db.getAllClients(),
    //根据id删除Client对象
    DBProvider.db.deleteClient(item.id);
    //更新性别
    DBProvider.db.updateSex(item);
    //随机取测试数据中的一条数据作为Client对象
    Client rnd = clients[math.Random().nextInt(clients.length)];
    //新增加一个Client对象
    await DBProvider.db.insertClient(rnd);
    

    相关文章

      网友评论

        本文标题:Flutter常见知识点汇总

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