Flutter版本玩Android开发记录(一)

952 阅读4分钟

前言

从今天开始记录一下目前正在做的Flutter版本的玩Android开发过程,整个系列的文章没有时间限制,边开发边记录,完成部分记录一部分。

整体结构

先来看一下整个app的结构

这个结构比较简单,这个结构是根据玩Android网站的目录导航从里边抽取了几个比较常用的。后续写到每个部分的时候会有一个针对每一部分的相对详细的结构图。

启动页

今天是这个系列的第一篇文章,先来介绍一下启动页的开发过程以及实现的效果。启动页的实现思路是 先显示原生的闪屏页,然后再用Flutter实现一个带有倒计时功能的过渡页,倒计时完成之后跳转到首页。之所以这样设计是因为,第一,没有找到合适的方法干掉原生的闪屏页直接跳到过渡页,第二,想先把首页用到的一些数据预先在过渡页请求下来供后续使用。按照这个思路,启动页的实现主要分为以下三个部分:

1. 修改原生的闪屏页

这不分可以参考我之前的一篇文章Flutter更换APP图标和启动页图标,里边有介绍怎么修改Android和IOS启动页的图标,这个项目也是按照这种方式修改闪屏页的。

2. 创建过渡页

之所以设计这个过渡页是因为我想通过这个页面预先请求一部分接口数据给后面的其他页面使用,所以这个页面就会有两个主要功能,第一:倒计时,第二:请求部分接口数据。 首先看一下过渡页,因为要赶进度,所以过渡页比较简陋,先凑合看吧。这个页面主要是倒计时跳转首页和下载首页轮播图数据的功能,使用的是Stack和Positioned层叠布局。核心代码如下:

import 'package:flutter/material.dart';
import 'dart:async';
import 'colors_config.dart';

//启动页

class SpalashPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Stack(
      //Stack层叠布局
      alignment: AlignmentDirectional.topEnd,
      children: [
        Container(
          width: double.infinity,
          height: double.infinity,
          color: ColorsConfig.primaryColor,
          alignment: AlignmentDirectional.center,
          child: Text(
            "技术连接世界",
            style:
                TextStyle(color: Colors.white, decoration: TextDecoration.none),
          ),
        ),
        Positioned(
          child: CountDownWidget(),
          top: 30,
          right: 20,
          width: 60,
          height: 30,
        )
      ],
    );
  }
}

//倒计时 跳过 组件
class CountDownWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return CountDownWidgetState();
  }
}

class CountDownWidgetState extends State<CountDownWidget> {
  //倒计时起始时间
  int _countTime = 5;
  //计时器
  Timer _timer;
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          border: Border.all(width: 1, color: Colors.white)),
      child: GestureDetector(
        child: Text(
          "跳过",
          style: TextStyle(
            color: Colors.white,
            fontSize: 13,
            decoration: TextDecoration.none,
          ),
          textDirection: TextDirection.ltr,
        ),
        onTap: () {
          _timer.cancel();
          Navigator.pushNamed(context, "MainPage");
        },
      ),
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _startCountDownTime();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _timer.cancel();
  }

//倒计时功能
  void _startCountDownTime() {
    //计时器刷新频率,每隔1s刷新一次
    const dura = Duration(seconds: 1);
    var callback = (timer) => {
          setState(() {
            if (_countTime < 1) {
              //取消倒计时,并且跳转到首页
              _timer.cancel();
              Navigator.pushNamed(context, "MainPage");
            } else {
              _countTime--;
            }
          })
        };
    //计时器实例
    _timer = Timer.periodic(dura, callback);
  }
}

3.过渡页网络请求

上面已经完成了过渡页的UI搭建,接下来我们在过渡页把首页轮播图用到的数据先请求下来。网络部分使用DIO作为我们的网络请求库。 整个请求过程分为以下几步:
1. 导入、配置DIO库
关于DIO的使用可以参考我的另一篇文章Flutter网络请求库DIO的使用
2. 创建bean类 我们的bean类通过在线解析JSON的方式生成,在创建bean类之前,先通过postman将banner图的接口数据请求下来,然后在线生成bean类。 banner图数据对应的bean类如下:
HomeBanner.dart

import 'dart:convert';

HomeBanner bannerFromJson(String str) => HomeBanner.fromJson(json.decode(str));

String bannerToJson(HomeBanner data) => json.encode(data.toJson());

class HomeBanner {
  HomeBanner({
    this.data,
    this.errorCode,
    this.errorMsg,
  });

  List<Datum> data;
  int errorCode;
  String errorMsg;

  factory HomeBanner.fromJson(Map<String, dynamic> json) => HomeBanner(
        data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
        errorCode: json["errorCode"],
        errorMsg: json["errorMsg"],
      );

  Map<String, dynamic> toJson() => {
        "data": List<dynamic>.from(data.map((x) => x.toJson())),
        "errorCode": errorCode,
        "errorMsg": errorMsg,
      };
}

class Datum {
  Datum({
    this.desc,
    this.id,
    this.imagePath,
    this.isVisible,
    this.order,
    this.title,
    this.type,
    this.url,
  });

  String desc;
  int id;
  String imagePath;
  int isVisible;
  int order;
  String title;
  int type;
  String url;

  factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        desc: json["desc"],
        id: json["id"],
        imagePath: json["imagePath"],
        isVisible: json["isVisible"],
        order: json["order"],
        title: json["title"],
        type: json["type"],
        url: json["url"],
      );

  Map<String, dynamic> toJson() => {
        "desc": desc,
        "id": id,
        "imagePath": imagePath,
        "isVisible": isVisible,
        "order": order,
        "title": title,
        "type": type,
        "url": url,
      };
}

3. 请求banner数据 由于网络请求是一个耗时操作,所以可以放到异步任务中执行。FutureBuilder是Flutter中一个实用并且功能强大的异步组件,可以把我们的网络请求放到这里边执行。

child: FutureBuilder(
            //get方式请求banner数据
            future: dio.get("/banner/json"),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                Response response = snapshot.data;
                //解析请求回来的数据
               
              }
              return Text(
                "技术连接世界",
                style: TextStyle(
                  color: Colors.white,
                  decoration: TextDecoration.none,
                ),
              );
            },
          ),

4. 解析banner数据
接口请求回来的数据一般都是json格式的,上面我们自己创建了一个HomeBanner类,这一步就需要我们把接口返回的json数据解析到HomeBanner中。 因为Flutter不支持反射,所以并没有提供类似FastJson之类的工具,这就需要我们自己上前面介绍的那样自己去创建bean类。但是Flutter提供了的解析方法json.decode()可以把json数据转换成一个Map,然后我们再调用HomeBanner.fromJson()就可以json数据转成我们需要的bean类了。

//解析请求回来的数据
Map bannerMap = json.decode(response.toString());
banner = HomeBanner.fromJson(bannerMap);

经过这几步我们的启动页就基本搭建完成了,并且预先请求了部分数据,这些数据将来要提供给首页banner轮播图使用,接下来就是搭建主界面的过程了,将在下一节介绍。

结语

记录码农生活,做一个快乐的coder。