Flutter练手项目--玩安卓

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

先上图吧

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

一、页面

主入口很简单,几句代码

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: pub.flutter-io.cn/flutter

项目地址

#源码地址

转载于:https://juejin.im/post/5cff3f27f265da1b6d401b6c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值