美文网首页
Flutter练手项目--玩安卓

Flutter练手项目--玩安卓

作者: Dale_Dawson | 来源:发表于2019-06-11 11:59 被阅读0次

    Flutter环境在很久之前就搭建好了,迟迟没有体验一把,发布会称Flutter将要一统天下,是时候体验一把Flutter了,还是借助玩安卓api接口来练手。

    先上图吧

    首页.jpg
    体系.jpg
    搜索.jpg
    搜索结果.jpg
    项目.jpg
    我的.jpg

    大致就分为 首页、体系、热搜、项目、我的、五大板块。

    一、页面

    主入口很简单,几句代码

    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: '玩安卓',
          debugShowCheckedModeBanner: false, //去掉页面右上角的debug标识
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MainPage(),
        );
      }
    }
    
    很多时候写完页面右上角还有个debug标识,添加上以下代码就可以去掉。
    debugShowCheckedModeBanner: false, //去掉页面右上角的debug标识
    

    以上代码中home后面的MainPage()才是真正的主页,我们来瞧一瞧

    页面下方的五个TAB添加方式

      bottomNavigationBar: BottomNavigationBar(
            // 底部导航
            items: <BottomNavigationBarItem>[
              new BottomNavigationBarItem(
                  icon: Icon(Icons.home), title: Text('首页')),
              new BottomNavigationBarItem(
                  icon: Icon(Icons.layers), title: Text('体系')),
              new BottomNavigationBarItem(
                  icon: Icon(Icons.search), title: Text('热搜')),
              new BottomNavigationBarItem(
                  icon: Icon(Icons.folder), title: Text('项目')),
              new BottomNavigationBarItem(
                  icon: Icon(Icons.person), title: Text('我的')),
            ],
            type: BottomNavigationBarType.fixed,
            currentIndex: _selectedIndex,
            fixedColor: Colors.blue,
            onTap: (index) {
              setState(() {
                _selectedIndex = index;
              });
            },
          ),
    

    顺便说下每个页面的状态保存方式

    class ListPageState extends State<ListPage> with AutomaticKeepAliveClientMixin
    @override
      // TODO: implement wantKeepAlive
      bool get wantKeepAlive => true;
    

    先实现AutomaticKeepAliveClientMixin然后重写@override bool get wantKeepAlive => true这个方法

    二、数据请求及解析

    1.数据请求

    数据请求这块,我用的是dio
    使用方法:
    1.先在pubspec.yaml中添加 dio: ^2.1.2 #网络框架
    2.点击编辑器右上方的Package get
    3.可以愉快的玩耍了
    来一个最简单的get请求

    import 'package:dio/dio.dart';
    Dio dio = new Dio();
    Response response=await dio.get("https://www.google.com/");
    print(response.data);
    

    2.数据解析

    数据解析用的是 json_serializable: ^2.0.0
    然后配合android studio 工具自动生成bean对象


    3.下拉刷新上拉加载很多

    刷新这块用的是
    flutter_refresh: ^0.0.2
    flutter_spinkit: "^3.1.0"
    具体使用方法:

    // 顶部刷新
      Future<Null> onHeaderRefresh() {
        return new Future.delayed(new Duration(seconds: 2), () {
          setState(() {
            pageIndex = 0;
            bannerList.clear();
            homeList.clear();
            this.getBannerList();
            this.getHomeList();
          });
        });
      }
    
    // 底部刷新
      Future<Null> onFooterRefresh() async {
        return new Future.delayed(new Duration(seconds: 2), () {
          setState(() {
            pageIndex += 1;
            this.getHomeList();
          });
        });
      }
    
    Widget buildCustomScrollView() {
        return new Refresh(
            onFooterRefresh: onFooterRefresh,
            onHeaderRefresh: onHeaderRefresh,
            childBuilder: (BuildContext context,
                {ScrollController controller, ScrollPhysics physics}) {
              return new Container(
                  child: new ListView.builder(
                      physics: physics,
                      controller: controller,
                      itemCount: homeList.length + headerCount,
                      itemBuilder: (BuildContext context, int index) {
                        if (index == 0) {
                          return buildBanner();
                        } else {
                          return buildList(homeList[index - headerCount]);
                        }
                      }));
            });
      }
    

    接下来我们看下主页里面某个单页面的具体实现,就首页吧

    import 'package:banner_view/banner_view.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_refresh/flutter_refresh.dart';
    import 'package:flutter_wananzhuo/bean/Api.dart';
    import 'package:flutter_wananzhuo/bean/BannerItem.dart' as bannerItem;
    import 'package:flutter_wananzhuo/bean/HomeItem.dart' as homeItem;
    import 'package:flutter_wananzhuo/utils/HttpUtil.dart';
    import 'package:flutter_wananzhuo/utils/NavigatorUtil.dart';
    
    class HomePage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => new _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      List<bannerItem.BannerData> bannerList = [];
      List<homeItem.HomeItemDataData> homeList = [];
      final int headerCount = 1;
      int pageIndex = 0;
      var bannerIndex = 0;
    
    //  final int pageSize = 20;
    
      @override
      void initState() {
        super.initState();
        getBannerList();
        getHomeList();
      }
    
    // 顶部刷新
      Future<Null> onHeaderRefresh() {
        return new Future.delayed(new Duration(seconds: 2), () {
          setState(() {
            pageIndex = 0;
            bannerList.clear();
            homeList.clear();
            this.getBannerList();
            this.getHomeList();
          });
        });
      }
    
    // 底部刷新
      Future<Null> onFooterRefresh() async {
        return new Future.delayed(new Duration(seconds: 2), () {
          setState(() {
            pageIndex += 1;
            this.getHomeList();
          });
        });
      }
    
    //获取轮播图接口
      void getBannerList() async {
        var response = await new HttpUtil().get(Api.BANNER_LIST);
        var item = new bannerItem.BannerItem.fromJson(response);
        bannerList = item.data;
        setState(() {});
      }
    
      void getHomeList() async {
        var response = await new HttpUtil()
            .get(Api.HOME_LIST + pageIndex.toString() + "/json");
        var item = new homeItem.HomeItem.fromJson(response);
        if (pageIndex == 0) {
          homeList = item.data.datas;
        } else {
          homeList.addAll(item.data.datas);
        }
        setState(() {});
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
            appBar: AppBar(
              elevation: 0,
              title: new Text("玩安卓"),
            ),
            body: buildCustomScrollView());
      }
    
      Widget buildCustomScrollView() {
        return new Refresh(
            onFooterRefresh: onFooterRefresh,
            onHeaderRefresh: onHeaderRefresh,
            childBuilder: (BuildContext context,
                {ScrollController controller, ScrollPhysics physics}) {
              return new Container(
                  child: new ListView.builder(
                      physics: physics,
                      controller: controller,
                      itemCount: homeList.length + headerCount,
                      itemBuilder: (BuildContext context, int index) {
                        if (index == 0) {
                          return buildBanner();
                        } else {
                          return buildList(homeList[index - headerCount]);
                        }
                      }));
            });
      }
    
      Widget buildList(homeItem.HomeItemDataData item) {
        return new Card(
          child: new InkWell(
            onTap: () {
              NavigatorUtil.toDetails(context, item.link, item.title);
            },
            child: new ListTile(
              title: new Row(
                children: <Widget>[
                  new Text(item.author,
                      textAlign: TextAlign.left,
                      style: new TextStyle(color: Colors.grey, fontSize: 13)),
                  new Text(item.niceDate,
                      textAlign: TextAlign.right,
                      style: new TextStyle(color: Colors.grey, fontSize: 13))
                ],
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
              ),
              subtitle: new Column(
                children: <Widget>[
                  new Text(item.title,
                      textAlign: TextAlign.left,
                      style: new TextStyle(color: Colors.black, fontSize: 15)),
                  new Text(
                    item.superChapterName + "/" + item.chapterName,
                    style: new TextStyle(color: Colors.blue, fontSize: 13),
                  )
                ],
                crossAxisAlignment: CrossAxisAlignment.start,
              ),
            ),
          ),
        );
      }
    
      Widget buildBanner() {
        return new Container(
          padding: EdgeInsets.all(5),
          child: bannerList.length > 0
              ? new BannerView(
                  bannerList.map((bannerItem.BannerData item) {
                    return new GestureDetector(
                        onTap: () {
                          NavigatorUtil.toDetails(context, item.url, item.title);
                        },
                        child: new Image.network(
                          item.imagePath,
                          fit: BoxFit.cover,
                        ));
                  }).toList(),
                  cycleRolling: false,
                  autoRolling: true,
                  indicatorMargin: 8.0,
    //              indicatorNormal: this._indicatorItem(Colors.white),
    //              indicatorSelected:
    //                  this._indicatorItem(Colors.white, selected: true),
    //              indicatorBuilder: (context, indicator) {
    //                return this._indicatorContainer(indicator);
    //              },
                  onPageChanged: (index) {
                    bannerIndex = index;
                  },
                )
              : new Container(),
          width: double.infinity,
          height: 200.0,
        );
      }
    }
    
    

    其他页面的实现都大同小异,可以去看源码

    三、遇到的问题

    1.数据还没加载出来,界面报红色错误

    解决:
    用一个变量控制是否在加载完成,未加载完成先显示加载页面
    举个栗子:

     @override
      Widget build(BuildContext context) {
        return isLoading
            ? SpinKitCircle(
                itemBuilder: (_, int index) {
                  return DecoratedBox(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(5.0),
                      color: Colors.grey,
                    ),
                  );
                },
              )
            : new Refresh(
                onFooterRefresh: onFooterRefresh,
                onHeaderRefresh: onHeaderRefresh,
                childBuilder: (BuildContext context,
                    {ScrollController controller, ScrollPhysics physics}) {
                  return new Container(
                  ));
                });
      }
    

    2.webview控件加载不出有些网页(报错:net err_cleartext_not_permitted)

    解决:
    从Android 9.0(API级别28)开始,默认情况下禁用明文支持。因此http的url均无法在webview中加载
    在android manifest.xml中添加

    <?xml version="1.0" encoding="utf-8"?>
       <manifest ...>
           <uses-permission android:name="android.permission.INTERNET" />
           <application
               ...
               android:usesCleartextTraffic="true"
               ...>
               ...
           </application>
       </manifest>
    

    3.某些控件(如Container)没有自带的点击事件

    解决:
    外面套一层

    GestureDetector
    onTap: () {
    Navigator.push(context,new MaterialPageRoute(
    builder: (context) =>
    new DetailsPage(item.link, item.name)));
    });
    

    4.需要使用什么库,搜索一下

    packages: https://pub.flutter-io.cn/flutter

    项目地址

    源码地址

    相关文章

      网友评论

          本文标题:Flutter练手项目--玩安卓

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