Flutter 知识点汇总

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: [
        Container(
          color: Colors.white,
          height: 200,
        ),
        SizedBox(height: 10,),
        DiscoverCell(imageName: 'images/微信支付1.png',title: '支付',),
      ],
    ),
  ),
),

3.Image设置圆角

 

Row(
children: [
  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 _buildItem(String imgAss, String title) {
    return PopupMenuItem(
      child: Row(
        children: [
          Image(
            image: AssetImage(imgAss),
            width: 20,
          ),
          Container(
            width: 20,
          ),
          Text(
            title,
            style: TextStyle(color: Colors.white),
          ),
        ],
      ),
    );
  }

//回调方法
  List> _buildPopupMenuItem(BuildContext context) {
    return >[
      _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: [
            new GridView.count(
             padding: EdgeInsets.all(0),
            physics: new NeverScrollableScrollPhysics(),//增加
            shrinkWrap: true,//增加
            crossAxisCount: 3,
            children:[]
          ],
        ),

① 处理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
    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: [
            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 可以添加约束

Flutter 知识点汇总_第1张图片

TextOverflow.ellipsis 不生效

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

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

 

class UserChatList {
  int doType;
  UserChatList({this.doType});
  UserChatList.fromJson(Map json) {
    doType = json['do'];
  }

21.聊天消息UI搭建

效果图

Flutter 知识点汇总_第2张图片

聊天消息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: [
          Stack(
            overflow: Overflow.visible,
            children: [
              // 消息
              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: [
                      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: [
          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: [
            Stack(
              overflow: Overflow.visible,
              children: [
                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: [
                        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();
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
theme(主题)   ThemeData
debugShowMaterialGrid(调试显示材质网格) bool

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

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

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

 

Future future;

  @override
  void initState() {
    super.initState();
    future=getInt();
  }

  FutureBuilder(
    future: future,
    builder: (context, snapshot) {
      return ...;
    }
  ),

  Future 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: [
          Container(
            margin: EdgeInsets.only(
                top: ScreenUtil().setHeight(17)),
            child: Row(
              children: [
                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: [
          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 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 {
  @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(
      onSelected: (ConferenceItem result) {},
      itemBuilder: (BuildContext context) =>//菜单项构造器
      >[
        const PopupMenuItem(//菜单项
          value: ConferenceItem.AddMember,
          child: Text('添加成员'),
        ),
        const PopupMenuItem(
          value: ConferenceItem.LockConference,
          child: Text('锁定会议'),
        ),
        const PopupMenuItem(
          value: ConferenceItem.ModifyLayout,
          child: Text('修改布局'),
        ),
        const PopupMenuItem(
          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 items = new List.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> 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> 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: [
            //显示传递参数值
            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 json) => Client(
        id: json["id"],
        name: json["name"],
        age: json["age"],
        sex: json["sex"] == 1,
      );

  //将数据模型转换成JSON
  Map 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 get database async {
    //使用单例模式创建Database对象
    if (_database != null) {
      return _database;
    }
    _database = await initDB();
    return _database;
  }

  //初始化数据库
  initDB() async {
    //获取文档目录对象
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    //获取默认数据库位置(在Android上,它通常是data/data//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> getAllClients() async {
    final db = await database;
    var res = await db.query("Client");
    List 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);



作者:StevenHu_Sir
链接:https://www.jianshu.com/p/286c8beacb11
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(【Flutter点滴知识,】)