Flutter練手專案--玩安卓

Dale_Dawson發表於2019-06-11

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物件

Flutter練手專案--玩安卓

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

專案地址

#原始碼地址

相關文章