Flutter练手项目--玩安卓

2019-06-11  本文已影响0人  Dale_Dawson

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

项目地址

源码地址

上一篇下一篇

猜你喜欢

热点阅读