美文网首页FlutterFlutter
Flutter实战(百姓生活+)

Flutter实战(百姓生活+)

作者: 十年之后_b94a | 来源:发表于2020-06-25 11:43 被阅读0次

    1、风格

    我们之前的风格都是material风格

    import 'package:flutter/material.dart';
    

    其实flutter还自带一种风格cupertino

    import 'package:flutter/cupertino.dart';
    
    Icon(CupertinoIcons.home)//cupertino风格的图标
    

    2、函数返回值不确定

    使用Future定义

    Future _test(){
      return ***;
    }
    

    3、解决内容越界

    使用SingleChildScrollView解决越界问题,该组件是越界滚动效果。

    4、异步请求改变界面的组件

    使用FutureBuilder组件

    FutureBuilder(builder: (context,snapshot){
      if(snapshot.hasData){//判断异步返回有没有值
    
      }else{
    
      }
    },future:getHomepageContent()),
    

    使用FutureBuilder组件会有一个问题,当里面的子组件有多个的时候,往往我们更新的数据只需要刷新一个子组件就好了,但是使用FutureBuilder会导致所有的子组件都会刷新
    解决办法Flutter开发性能提升之:如何避免Widget重复Build

    5、屏幕适配

    随着移动手机的屏幕尺寸越来越多,我们怎么能够适配
    使用flutter_ScreenUtilflutter_ScreenUtil
    注意如果添加跟适配ScreenUtil.init需要放在TabPage中

      @override
      Widget build(BuildContext context) {
        ScreenUtil.init(context, width: 750, height: 1334, allowFontScaling: false);
        return Container(
          width:ScreenUtil.setWidth(250),
          height:ScreenUtil.setHeight(250)
        );
      }
    

    6、点击事件调用手机的拨打电话

    使用url_launcher 调用拨打电话
    他可以打开网页、手机拨号、邮箱、短信

    dependencies:
      url_launcher: ^5.4.10
    
    import 'package:url_launcher/url_launcher.dart';
    _launchURL() async {//打开网页
      const url = 'https://flutter.dev';
      if (await canLaunch(url)) {
        await launch(url);
      } else {
        throw 'Could not launch $url';
      }
    }
    
      void _makePhoneCall(String phone) async {
        注意拨打手机多了'tel'
        例如 拨打1888888888
        里面的值应该是tel:1888888888
        if (await canLaunch('tel:'+phone)) {
          await launch('tel:'+phone);
        } else {
          throw 'Could not launch $url';
        }
      }
    

    7、切换导航或者页面状态保持

    类似前端vuekeep-live效果 页面缓存
    使用继承AutomaticKeepAliveClientMixin这个类
    使用这个类有三点
    1、必须是statefulwidget组件
    在我们需要保持状态的页面中加入

    class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{}
    

    2、必须重构wantKeepAlive方法
    在我们需要保持状态的页面中加入

    @override
    bool get wantKeepAlive => true;
    

    3、需要在tabpage页面中body中套上IndexedStack
    这个页面是我们的导航切换页面

    class TabsPage extends StatefulWidget {
      TabsPage({Key key}) : super(key: key);
    
      @override
      _TabsPageState createState() => _TabsPageState();
    }
    
    class _TabsPageState extends State<TabsPage> {
        int curIndex = 0;
    
        List<BottomNavigationBarItem> _list_bottom_item = [
            BottomNavigationBarItem(icon: Icon(CupertinoIcons.home), title: Text("首页")),
            BottomNavigationBarItem(icon: Icon(CupertinoIcons.search), title: Text("分类")),
            BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.shopping_cart), title: Text("购物车")),
            BottomNavigationBarItem(
                icon: Icon(CupertinoIcons.profile_circled), title: Text("会员中心"))
        ];
    
        List<Widget> list_page = [HomePage(), ListPage(), ShopCardPage(), UserPage()];
    
        _TabsPageState();
    
        @override
        Widget build(BuildContext context) {
        ScreenUtil.init(context, width: 750, height: 1334, allowFontScaling: false);
            return Scaffold(
          backgroundColor: Color.fromRGBO(244, 245, 245, 1),
                bottomNavigationBar: BottomNavigationBar(
                items: this._list_bottom_item,
                onTap: (i) {
                    setState(() {
                        this.curIndex = i;
                    });
                },
                currentIndex: this.curIndex,
                type: BottomNavigationBarType.fixed,
                ),
                body: IndexedStack(重点
                  index:this.curIndex,当前索引
                  children:this.list_page
                ),
            );
        }
    }
    

    8、使用GridView/ListView.builder的避坑

    问题1、在使用GridView/ListView.builder如果这两个组件是被放在Cloumn、Row中作为子组件那么将会报错

    解决1、设定高度
    当确定GridView/ListView.builder这个两个组件的高度时,我们可以在外面包裹一层Container并设置高度

    Column(
      children:[
        Container(
          height:500.0,
          child:GridView.count()
        )
      ]
    )
    

    解决2、换一种布局实现
    可以使用Wrap流布局实现

    Column(
      children:[Wrap(
        children:[]
      )]
    )
    

    问题2、当listView.buider/GridView.count()循环的数据为空的时候 会报错

    遇到的情况,例如要从后台获取list数据使用listView.buider/GridView.count()渲染,会报错一瞬间(取决于获取数据的时间)

    解决方法
    使用三木运算判断数据是否为空

    List data;
     body: new Center(
      重点
        child: data == null ? Container() : new ListView.builder(
            itemCount: data.length,
            itemBuilder: (BuildContext context, int position) {
              return new ListTile(
                title: new Text('${data[position]['name']}'),
              );
            }),
      ),
    

    9、上拉加载/下拉刷新

    使用flutter_pulltorefresh

    10、通过后台返回的数据我们可以建立模型

    建立模型就类似于java中的数据模型
    dynamic未知类型

    案例:例如后台返回的数据格式

    {
        "code" : 0,
        "message" : "",
        "result" : [
            {
                "userId" : "001",
                "userName" : "十年之后",
                "email" : "592885211@qq.com"
            },
            {
                "userId" : "002",
                "userName" : "十年之后2",
                "email" : "5928852112@qq.com"
            },
            {
                "userId" : "003",
                "userName" : "十年之后3",
                "email" : "5928852113@qq.com"
            },
            {
                "userId" : "004",
                "userName" : "十年之后4",
                "email" : "5928852114@qq.com"
            }
            ***
        ]
    }
    

    构建数据模型

    //这是每个Map的模型
    class UserModel{
      String userId;
      String userName;
      String email;
      UserModel({//构造函数
        this.userId,
        this.userName,
        this.email
      });
      //工厂模式
      factory UserModel.formJson(dynamic json){
        return UserModel(//其实这一步是调用构造函数
          userId:json['userId'],
          userName:json['userName'],
          email:json['email'],
        )
      }
    }
    //有了每个Map的模型,我就在建一个List Map模型
    
    class UserModelList{
      List<UserModel> modelList; //每一个List的元素 都是UserModel类型
      UserModelList(this.modelList);//构造函数
      //工程模式
      factory UserModelList.formJson(List json){ //注意这里 是list 我们传进来的肯定是个数据
        return UserModelList(
          json.map((i)=>UserModel.formJson(i)).toList()//遍历每一项
        );
      }
    }
    

    使用数据模型

    在需要模型的页面引入刚刚建立的模型
    var data = await getDataApi()//假如这是借口返回的数据
    UserModelList userList = UserModelList.formJson(data);
    //需要保证data是个数组,因为我们在创建UserModelList 定义了传入List 
    userList.modelList;//就是我们的数据了 这个modelList就是上面的变量
    

    11、 状态管理

    类似于Vuex用于共享数据的管理
    使用provider
    请注意谷歌官方的状态管理插件已被弃用

    dependencies:
      provider: ^4.1.3
    

    我们在lib目录下创建文件夹provide,继续在provide创建counter.dart文件
    我们不按照文档的例子来 这次我们改变Map中的数据

    //count.dart
    import 'package:flutter/material.dart';
    class Counter with ChangeNotifier{
      Map userInfo = {
        "userName" : "十年之后"
      };
      //创建改变数据的函数通过调用该函数我们做到改变数据
      void setUserName(String name){
        userInfo['userName'] = name;
        notifyListeners();//通知去监听视图改变
      }
    }
    

    然后我们在main.dart中改造

    import 'package:provider/provider.dart';
    import './provide/counter.dart';
    void main() {
      runApp(
        MultiProvider(
          providers:[
            ChangeNotifierProvider(
              create:(_)=>Counter())
          ],
          child:MyApp()
        )
      );
    }
    

    最后一步 我们在需要用到数据的页面中使用

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import '../provide/count.dart';
    class ListPage extends StatelessWidget {
      const ListPage({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
           child: Center(
             child: Column(
               children: <Widget>[
    //用法在这里
                 Consumer<Counter>(builder:(context,t,child){
                    return Text(t.userInfo["userName"])
                  })
    //用法在这里
                 RaisedButton(onPressed: () => Provider.of<Counter>(context, listen:false).setUserName('十年之后V5');child: Text("改变用户名"),)
               ],
             ),
           ),
        );
      }
    }
    

    如果我们要监听某个存储的值,也就是:改了某个数据,我们视图也立即改变用Provider.of<Counter>(context, listen:false).get_userInfo["userName"]该方法并不能做到监听数据的改变,我们应该使用

    context.watch<Counter>().get_userInfo["userName"];
    

    结束语:我们在创建个页面 使用该状态管理 会发现 视图也会一起改变

    12、分类页面点击左侧右侧内容相应改变

    因为我们设计的内容结构是

    Row(
      chidren : [
        Left(),
        Right(),
      ]
    )
    

    很明显这两个组件没有通信,但是我们又有数据的交互,我们可以使用状态管理来实现

    
    class ChildCategory with ChangeNotifier, DiagnosticableTreeMixin {
      List childCategoryList = [];
    
      List get get_childList =>childCategoryList;
    
      void setChildCategoty(List list){
        childCategoryList = list;
        notifyListeners();
      }
    
      @override
      void debugFillProperties(DiagnosticPropertiesBuilder properties) {
        super.debugFillProperties(properties);
        properties.add(ObjectFlagProperty('get_childList', get_childList));
      }
    }
    
    
    实现思路左侧点击改变状态管理中的数据
    context.read<ChildCategory>().setChildCategoty(this.cetegorryLeftData[index].bxMallSubDto);
    //this.cetegorryLeftData[index].bxMallSubDto这个就是我们需要重新复制的数据
    
    右侧获取改变的数据
    我们可以在全局定义一个变量
    然后在
    @override build 进行赋值
    listRightData = context.watch<ChildCategory>().get_childList;
    

    13、企业级路由

    使用fluro
    插件

    dependencies:
     fluro: "^1.6.3"
    
    import 'package:fluro/fluro.dart';
    

    然后我们在MyApp builder函数中

    class MyApp extends StatelessWidget {
     @override
      Widget build(BuildContext context) {
      final router = Router();}
    }
    

    然后我们创建一个路由文件,所有的路由Handler配置都放在这里面

    import 'package:flutter/material.dart';
    import 'package:fluro/fluro.dart';
    import '../pages/details_page.dart';//这是我们需要跳转的页面
    
    Handler detailsHanderl =Handler(
      handlerFunc: (BuildContext context,Map<String,List<String>> params){
        String goodsId = params['id'].first;
        print('index>details goodsID is ${goodsId}');
        return DetailsPage(goodsId);
    
      }
    );
    

    然后我们在创建路由配置文件

    import 'package:flutter/material.dart';
    import 'package:fluro/fluro.dart';
    import './route_handler.dart' //导入我们刚刚配置的Handler
    
    class Routes{
      static String rootPath = "/";
      static String detailsPath = "/details";//商品详情的路由
      static void configureRoutes(Router router){
        //配置下404 空路由
        router.notFoundHandler= Handler(
          handlerFunc:(BuildContext context,Map<String,List<String>> params){
            print("Not Found");
            return TabsPage();//可以跳转到我们写的tabs页面中
          }
        )
        router.define(detailsPath,Handler : detailsHanderl)
      }
    } 
    

    为了方便我们可以静态化路由一下(实例化)
    创建一个文件application.dart

    import 'package:fluro/fluro.dart';
    class Application{
      static Router router;
    }
    

    再然后我们在MyAppbuilder中

    class MyApp extends StatelessWidget {
       @override
      Widget build(BuildContext context) {
        final router = Router();
        Routes.configureRoutes(router);//重点
        Application.router=router;//重点
        return MaterialApp(
          onGenerateRoute: Application.router.generator,//重点
        );
      }
    }
    

    路由跳转

    Application.router.navigateTo(context, "/detail?id=456456&ab=a456");
    

    如果我们跳转至导航页面即Tab页面不希望他有返回页面按钮

    Application.router.navigateTo(context, "/tab?curIndex=2",clearStack: true);
    

    14、html转换widget

    使用插件flutter_html

    dependencies:
      flutter_html: ^1.0.0
    
    import 'package:flutter_html/flutter_html.dart';
    Html( //Html组件
      data: dataDic['information'], //html数据
    )
    

    15、数据持久化

    类似于前端的localstoare
    使用谷歌自带shared_preferences
    注意此shared_preferences不能缓存List<Map>类型的数据
    即:[{}]不能被缓存我们可以toString

    dependencies:
      shared_preferences: ^0.5.7+3
    
    import 'package:flutter/material.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class ShopCardPage extends StatefulWidget {
      ShopCardPage({Key key}) : super(key: key);
    
      @override
      _ShopCardPageState createState() => _ShopCardPageState();
    }
    
    class _ShopCardPageState extends State<ShopCardPage> {
    //重点
      Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
      List<String> testList = [];
      @override
      Widget build(BuildContext context) {
        getList();//重点
        return Scaffold(
          appBar: AppBar(title: Text("购物车"),centerTitle: true,),
          body: Container(
            child: Column(
              children: <Widget>[
                Container(
                  width: double.infinity,
                  height: ScreenUtil().setHeight(500.0),
                  child: ListView.builder(itemBuilder: (context,index){
                    return Container(
                      margin: EdgeInsets.only(bottom: 10.0),
                      child: Text(this.testList[index]),
                    );
                  },itemCount: testList.length,),
                ),
                Row(
                  children: <Widget>[
                    RaisedButton(onPressed: ()=>this.setList(),child: Text("增加数据"),),
                    RaisedButton(onPressed: ()=>this.remList(),child: Text("删除数据"),),
                  ],
                )
              ],
            ),
          ),
        );
      }
      //获取数据 查
      void getList()async{
        final SharedPreferences prefs = await _prefs;//重点
        var infoData = prefs.getStringList("testInfo");
        setState(() {
          testList = infoData == null ? [] : infoData;
        });
      }
      //增
      void setList()async{
        final SharedPreferences prefs = await _prefs;//重点
        testList.add("十年之后_flutter");
        prefs.setStringList("testInfo", testList);
        this.getList();
      }
      //删
      void remList()async{
        final SharedPreferences prefs = await _prefs;//重点
        prefs.remove("testInfo");
        setState((){
          testList=[];
        });
      }
    }
    

    相关文章

      网友评论

        本文标题:Flutter实战(百姓生活+)

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