Flutter知识点集锦

251 阅读9分钟

IOS端

1. ios模拟器不弹键盘通过快捷键:command + shift + k。

Android端

其他

1. 自定义屏幕适配类HYSizeFit()时,使用之前需使用对应类初始化即HYSizeFit.init(),才可使用内部方法和属性如HYSizeFit.setPx()。(在这里可使用Dart2.6以后提供的extension方法)。
2. 通过api接收的json要转换成Flutter可以使用的model数据;
   或者也可以不用转换,直接 ["属性名"] 获取值即可。
3. 使用GestureDetector()组件解决要使空白区域产生点击效应,添加: behavior:HitTestBehavior.opaque。
4. initState()里面不可以有async方法,可以通过新建另外的异步方法,将此方法添加到initState()里面。
5. 四舍五入取2位小数:toStringAsFixed(2)---String类型
        转为double:double.parse()
   四舍五入取整数:toStringAsFixed(0)---String类型
     转为double:int.parse()
   其他类似;
6. 求时间间隔:
   time = DateTime.parse("2020-12-14 08:38:42").difference("2020-12-12 04:36:23")
   time.inDays: 按天数表示
   time.inhours: 按小时数表示
   time.inMinutes: 按分钟数表示
   time.inSeconds: 按秒数表示
7. 跳转页面传参数(一般参数是个map对象,比如某条数据详情)
     单个参数:
       传参:onTap: () {Navigator.of(context).pushNamed("/productionDetails", arguments: _productionData);},
       获参:final Map data = ModalRoute.of(context).settings.arguments;
     多个参数:
       传参:arguments传入的是一个Object,传多个参数,可以使用{}或者[]包裹;
       获参:final Map data = ModalRoute.of(context).settings.arguments;
           (用 final List data([]包裹即为数组) 或者 final Map data({}包裹即为map) 获取)。
8. 父子组件传值
   1) 父组件给子组件传值:
        父组件:class HYFather{
                 @override
                 Widget build(BuildContext context) {
                   return Container(
                     child: HYChild(data);  
                   
                     //  ----传递多个参数
                     child: HYChild(data1,data2);
                   );
                 }
               }
        子组件:class HYChild{
                 final Map data;
                 HYChild(this.data);
               
                 // ----接受多个参数
                 // final Map data1;
                 // final Map data2;
                 // HYChild(this.data1,this.data2)
               }
   2) 子组件通过回调通知给父组件:
   
9. ListView组件取消上下滑动: 添加属性:physics: NeverScrollableScrollPhysics()。
10. 关于ListView组件上拉加载、下拉刷新和api分页请求的代码安排:
    1) 上拉加载:
    -----stata里面定义监听器:ScrollController _controller;
    -----在ListView.builder里面添加 controller: _controller;
    -----在initState方法里面初始化监听器并且监听滚动的距离,如果大于api分页请求的数量,则请求下一页数据:
           _controller = ScrollController();
           _controller.addListener((){
             if(_controller.position.pixels >= _controller.position.maxScrollExtent){
               _offset += 7;
               _getProductionData();
             }
           })
    2) 下拉刷新:
    -----在ListVie.builder外面包裹RefreshIndicator();
    -----给其属性onRefresh添加实现方法 onRefresh: _onRefresh;
    -----实现_onRefresh方法:先把列表数据清空,然后插入最新请求的前10条,然后展示完毕之后继续上拉加载的逻辑。
    3) api分页请求:
       var data = await Http.request("production/list","GET",params: {"offset": _offset, "limit": _limit});
11. 基线对齐的属性:crossAxisAlignment: CrossAxisAlignment.baseline,textBaseline: TextBaseline.ideographic。
12. 属性和变量的区别?
    本质上一样,属性用于描述对象,变量更多用于逻辑运算。(属性也是变量,只不过属于对象)
13. 关于导航栏:
    使用IndexedStack来保持页面状态的优点就是配置简单,但是它也有很大的缺点:IndexedStack中管理的子页面在一开始就全部一次性加载出来了,不管有没有显示出来,然后通过index属性来确定到底显示哪一个页面。
14. 关于切换页面保持页面状态的解决方法:
    1) 使用IndexedStack()组件实现,它的作用是显示第index个child,其它child在页面上是不可见的,但所有child的状态都被保持,只需要将现在的body用IndexedStack包裹一层即可。
15. setState()方法只存在于StatefulWidget中。
16. Container()组件宽度:
    如果不给Container组件设置宽度,那么其宽度为取决于子组件的宽度;
    设置其宽度为满屏宽度:width: MediaQuery.of(context).size.width(或者 double.infinity)
17.
--使用shared_preferences用来本地存储的使用步骤:
    1) 安装依赖:shared_preferences: ^0.5.12
    2) 引入插件并且封装本地信息存储的公共类(以存token为例)
       import 'package:shared_preferences/shared_preferences.dart';
	   class Local {
         static String _token;  
         // 设置token
         static setToken(token) async{
           final p1 = await SharedPreferences.getInstance();
           await p1.setString(_token, token);
         }
         // 获取token
         static getToken() async{
           final p1 = await SharedPreferences.getInstance();
           return p1.getString(_token);
         }
         // 删除token
         static delToken() async{
           final p1 = await SharedPreferences.getInstance();
           p1.remove(_token);
         }
       }
    3) 使用:
       引入公共类;
       设置:Local.setToken(data["content"]["auth_token"]);
       获取:Local.getToken().then((res){
              if(res != null){
                setState(() {
          		  _token = res;
                });
      		  }
            });  //在StatefulWidget中
       删除:Local.delToken()
--关于添加多个属性:
  如果在该公共类中添加多个属性,现在的使用情况会导致只能设置一个属性,设置多个会出现覆盖则会变成最后一个属性的值的情况;现在的解决办法是shared_preferences设置一个,其他需要设置存储的变量在使用位置用shared_preferences原生实现。
18. 使用TextFormField()组件作为输入框时,app会自动获取鼠标焦点并且弹出键盘,设置点击空白处收起键盘:
      用GestureDetector()组件包裹当前最外层组件,然后添加
        behavior: HitTestBehavior.translucent,
        onTap: () {
          FocusScope.of(context).requestFocus(FocusNode());
        }
19. 去除手机模拟器右上角的Debug标签,在materialApp里面添加属性:debugShowCheckedModeBanner: false。
20. 关于页面初始化:
    1) 首页home.dart中,Scaffold()里面的bottomNavigationBar()组件的IndexedStack()的pages属性对应有哪些组件,那么app启动之后就会初始化这些页面,也会执行initState方法中的异步请求。
    2) 如果上面的某些异步请求带token,那么不能把异步请求放在initState方法中,可以放在点击登陆并且成功的按钮事件中,然后再传给home页面。
21. 关于抽象类和抽象方法的应用场景:(以下特点在 C++ 语言中可能有所区别)
    1) 抽象方法必须定义在抽象类中,即用关键字abstract修饰的方法;
    2) 继承抽象类之后,必须实现抽象类中的抽象方法;其他方法可以不实现;
    3) 抽象类一般适用在大型的api库或者工具库中。
22. 为什么Flutter设计的时候stateFulWidget的build方法放在state中?
    1) build出来的的widget是需要依赖State中的变量(状态/数据);
    2) 在Flutter运行过程中,widget是不断销毁和创建的,当自己的状态发生改变时,并不希望重新建一个新的State。
23. 关于widget的生命周期:(主要针对于statefulwidget)
    1) 生命周期的作用:
       初始化一些数据、变量、状态;发送网络请求;一些事件的监听比如controller添加添加监听事件;管理内存比如一些定时器、controller手动销毁。
    2) statefulwidget生命周期的执行顺序:(举例类名为HYHomeContent)
       第一是执行HYHomeContent的构造方法;
       第二是执行HYHomeContent的createState方法;
       第三是执行_HYHomeContentState的构造方法;
       第四是执行_HYHomeContentState的initState方法;
       第五是执行_HYHomeContentState的build方法;
       第六是执行_HYHomeContentState的dispose方法(页面销毁时调用);
       

List属性和方法

  • 属性
    lenght:数组长度
    first:数组第一个元素
    last:数组最后一个元素
    reversed:数组元素顺序相反
    isEmpty:是否为空
    isNotEmpty:是否为非空
    
  • 方法
    add():像数组添加元素
    addAll(list):给一个数组添加另一个新的数组
    insert(索引,值):给数组某指定位置添加某元素,其之后元素依次顺延
    insertAll(索引,数组):给数组某指定位置添加某数组,其之后元素依次顺延
    remove():删除指定值
    removeAt(索引):删除指定索引对应的值
    removeRange(索引,索引):删除指定索引范围的值
    removeLast():删除数组最后一个元素
    removeWhere((){}):删除满足某条件的元素,没有返回值,直接改变原数组
    clear():删除所有元素
    setRang(索引,索引,数组值):修改数组中某区间元素为指定值,其后元素依次顺延
    setAll(索引,数组值):修改指定索引后的数组值
    replaceRange(索引,索引,值):替换数组中某区间范围的值为某一个值
    fillRange(索引,索引,值):将数组中某区间范围的值每个都换成替换的值
    getRange(索引,索引):获取数组某区间范围的值
    sublist(索引,索引):获取指定区间的值(如果只有一个索引,则获取的是该索引到最后的值)
    any((){}):判断数组内是否有满足条件的元素
    every((){}):判断数组所有元素都满足某条件
    contains():判断数组是否包含某元素
    firstWhere((){},orElse:(){}):获取满足条件的第一个元素,如果未找到符合条件的元素,则进入orElse
    lastWhere((){},orElse:(){}):获取满足条件的最后一个元素,如果未找到符合条件的元素,则进入orElse
    indexWhere((){}):获取满足条件的第一个元素的索引,若不存在返回-1
    indexWhere((){},索引):从索引值开始获取满足条件的第一个元素的索引,若不存在返回-1
    lastIndexWhere((){}):获取满足条件的最后一个元素的索引,若不存在返回-1
    lastIndexWhere((){},索引):从索引值开始获取满足条件的最后一个元素的索引,若不存在返回-1
    indexOf():获取某值第一次出现的索引,若不存在返回-1
    indexOf(值,索引):从索引值开始获取某值第一次出现的索引,若不存在返回-1
    lastIndexOf():获取某值第一次出现的索引,若不存在返回-1
    lastIndexOf(值,索引):从索引值开始,获取某值第一次出现的索引,若不存在返回-1
    singleWhere((){},orElse:(){}):获取唯一元素,如果不存在,执行orElse;如果存在该元素但是出现次数不唯一,直接抛出错误进入catch
    join():把数组中的元素用指定字符拼接成字符串
    toSet():数组去重
    forEach():遍历数组
    map((e){return }):按指定条件返回一个新数组
    reduce((val,element){return }):返回数组元素总和(累加器)
    sort((a,b){return a-b;}):排序,按回调中return结果的正负数来排序
    

报错

1. Unhandled Exception: setState() or markNeedsBuild() called during build
  报错原因:父组件没初始化完,子组件就去操作initstate里面setState了。
  解决办法:在子组件的initState()方法中添加WidgetsBinding.instance.addPostFrameCallback((_){}),该函数体内放置setState的操作。
2. 在home页面的IndexedStack的children属性中写入的页面都会在进入home页面初始化,如果有涉及到需要从前一个页面点击跳转且传值过来获取值的情况,要判断初始化时值为null的情况。