flutter引导页、布局、定时器、网络异步数据更新

疫情刚好静下心来回归过去的工作,博主要开始学习flutter,博主会边学习边实战项目连续连贯实现,大约一周会出一篇,源码地址,开源不易,麻烦动手点星,谢谢,本开源不做商业使用,里面涉及用到api接口资源等只供学习,项目开发会分博客文章,请点击链接到对应的文章中浏览,建议其实万变不离其中,说到底还是离不开原生,学习之前往补充原生知识,本片主要讲的是引导页,其中涉及到技术点层叠布局、相对定位、容器、定时器、异步数据更新,再次感谢大家能耐心观看,谢谢!
@[TOC](Flutter 开始征途)

1 布局详解

1.1 Stack层叠布局

在flutter中层叠布局就是叠加效果,如果做过安卓的话,那么就知道FrameLayout,其实就依次往上一层view层叠效果

在这里插入图片描述

ios 最原始addSubview,其实就是一个层叠布局效果。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView * view1 = [[UIView alloc]initWithFrame:CGRectMake(20, 40, 200, 200)];
    view1.backgroundColor=[UIColor redColor];
    UIView * view2 = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    view2.backgroundColor=[UIColor greenColor];
    [view1 addSubview:view2];
    [self.view addSubview:view1];
}

下面是flutter的实现,children包含子组件数组,依次视图叠加效果,而Positioned是用来做绝对位置的,左右头底,四个点需要自己计算适配哦。

在这里插入图片描述

1.2 Align 相对布局详解

1.2.1 安卓相对布局

在安卓中大家应该很熟悉RelativeLayout,这可是我们安卓最常用最无敌布局神器,再复杂的业务都是浮云。

布局 方向
layout_centerHorizontal 水平居中
layout_alignParentRight 右对齐
layout_alignParentLeft 左对齐
layout_alignParentTop 顶部对齐
layout_alignBottom 居底部对齐
layout_centerInParent 居中
layout_centerVertical 竖向对齐

1.2.2 ios相对布局

ios我们开发的时候,我们用的Autolayout布局,解析如下:

UIView *subView = [[UIView alloc]init];
subView.backgroundColor = [UIColor greenColor];
[self.view addSubview:subView];
subView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1. constant:150]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.3 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.3 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1. constant:100]];

上面方法参数说明:
第一个参数:指定约束左边的视图view1
第二个参数:指定view1的属性attr1
第三个参数:指定左右两边的视图的关系relation
第四个参数:指定约束右边的视图view2
第五个参数:指定view2的属性attr2
第六个参数:指定一个与view2属性相乘的乘数multiplier
第七个参数:指定一个与view2属性相加的浮点数constant
依据的公式是:view1.attr1 = view2.attr2*multiplier +constant

布局 方向
NSLayoutAttributeLeft 视图的左边
NSLayoutAttributeRight 视图的右边
NSLayoutAttributeTop 视图的上边
NSLayoutAttributeBottom 视图的下边
NSLayoutAttributeLeading 视图的前边
NSLayoutAttributeTrailing 视图的后边
NSLayoutAttributeWidth 视图的宽度
NSLayoutAttributeHeight 视图的高度
NSLayoutAttributeCenterX 视图的中点的X值
NSLayoutAttributeCenterY 视图中点的Y值
NSLayoutAttributeBaseline 视图的基准线
NSLayoutAttributeNotAnAttribute 无属性

NSLayoutRelation的类型:
NSLayoutRelationLessThanOrEqual 关系小于或等于
NSLayoutRelationEqual 视图关系等于
NSLayoutRelationGreaterThanOrEqual 视图关系大于或等于
这里要说明一下,设置约束之前必须要求确保子视图添加到了父视图上了(如:[self.view addSubview:subView]),并且被约束的视图的translatesAutoresizingMaskIntoConstraints = NO,不然就会发生程序crash。

1.2.3 flutter Align相对布局

Align 组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的的宽高,定义如下:
首先你要在外部套一个容器,或者像安卓要套一个RelativeLayout一样,区别目前么发现如何自适应,必须设置区域大小。

布局 方向
Alignment.topLeft 头部左对齐
Alignment.topRight 头部右对齐
Alignment.topCenter 头部居中
Alignment.centerLeft 居中左对齐
Alignment.centerRight 居中右对齐
Alignment.center 居中对齐
Alignment.bottomLeft 底部左对齐
Alignment.bottomCenter 底部居中
Alignment.bottomRight 底部右对齐
new Positioned(
            child: Container(
              child: Align(
                child: Stack(
                  alignment: Alignment.center,
                  children: <Widget>[
                    Image.asset('images/splash_confirm.png'),
                    Text(
                      "立即开启",
                      style: TextStyle(
                          fontSize: 12,
                          color: Colors.white,
                          fontFamily: 'Raleway',
                          //2.不继承默认样式
                          decorationStyle: TextDecorationStyle.dashed),
                    )
                  ],
                ),
                alignment: Alignment.bottomCenter,
              ),
              margin: EdgeInsets.only(bottom: 100),
            ),
            height: pageTotalSize > 0 && (pageViewIndex == pageTotalSize - 1)
                ? height
                : 0,
            width: width)

alignment : 需要一个AlignmentGeometry类型的值,表示子组件在父组件中的起始位置。
AlignmentGeometry 是一个抽象类,它有两个常用的子类:Alignment和 FractionalOffset,我们将在下面的示例中详细介绍。
widthFactor和heightFactor是用于确定Align 组件本身宽高的属性;
它们是两个缩放因子,会分别乘以子元素的宽、高,最终的结果就是Align 组件的宽高。如果值为null,则组件的宽高将会占用尽可能多的空间。

2 定时器

启动页会有定时器,定时几秒后操作,下面会举例说明安卓、ios、flutter实现不同。

2.1 安卓定时器

第一种方式
Timer timer = new Timer(); //创建一个定时器对象
TimerTask task = new TimerTask()
timer.schedule(task,0,10000); //启动定时器
第二种方式
//参数1:计时总时间,参数2:每次扣除时间数

CountDownTimer cdt = new CountDownTimer(10000, 100)
          {  
                  @Override  
                  public void onTick(long millisUntilFinished)
                  {  
                  }  
                  @Override  
                  public void onFinish() {  
                  } 
         }; 
         cdt.start(); 

其它方式 alarmService、thread、Handler。

2.2 ios定时器

第一种

 NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(test) userInfo:nil repeats:YES];
    // 将定时器添加到runloop中,否则定时器不会启动
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    // 停止定时器
    [timer invalidate];

第二种

// 创建displayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test:)];
// 将创建的displaylink添加到runloop中,否则定时器不会执行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

// 停止定时器
[displayLink invalidate];
 displayLink = nil;

2.3 flutter 定时器

Timer定时器记得一定要在initState,这个函数就是初始化状态,你就可以理解为安卓的onCreate()生命周期、ios的initWithCoder状态一样,build就是一个构建widget,其实就可以理解为安卓xml,但是这里有很大区别,就是更新ui、初始化、initState()、didUpdateWidget()、setState()、didChangeDependencies()都会调用哦,当然布局上更新的数据怎么做,一定是在build构建写对应数据变量,其实思想就跟mvvm,数据驱动刷新ui一样。

@override
  void initState() {
    // TODO: implement initState
    super.initState();
    splashBuilder = fetchPost();
    initCountDown();
    WidgetsBinding.instance.addObserver(this);
  }
initCountDown() {
    // 只在倒计时结束时回调
    Timer.periodic(new Duration(seconds: 1), (timer) {
      if (timer.tick == 5) {
        setState(() {
          nextStr = '跳过';
        });
        timer.cancel();
        print(nextStr);
      } else {
        setState(() {
          nextStr = '${countTime--}s跳过';
        });
        print(nextStr);
      }
    });
  }
new Positioned(
            child: new GestureDetector(
              child: new Container(
                padding: const EdgeInsets.only(
                    left: 10, top: 2, right: 10, bottom: 2),
                decoration: new ShapeDecoration(
                    color: !isClicking1 ? Colors.white : Color(0xff898989),
                    shape: StadiumBorder(
                        side: BorderSide(
                            color: Color(0xff898989),
                            style: BorderStyle.solid,
                            width: 1))),
                child: Text(nextStr),
              ),
              onTap: () {
                print('onTap 跳过');
              },
              onTapUp: (TapUpDetails) {
                print('onTapUp 跳过');
                upDataButtonState(false);
              },
              onTapDown: (TapUpDetails) {
                print('onTapDown 跳过');
                upDataButtonState(true);
              },
              onTapCancel: () {
                print('onTapCancel 跳过');
                upDataButtonState(false);
              },
            ),
            top: 10 + statebar_height,
            right: 10)

3 网络异步更新

3.1 网络使用详解

目前在flutter使用最多就是Dio库,在安卓都是用的是Reftrofit,ios AFNetWork,好像git有个flutter的reftroft对应插件。dio链接

在这里插入图片描述

在这里插入图片描述

首先引用dio插件,我们要做一个启动页就要有网络申请,直接上dio代码,get获取图片集合,然后网络解析加载。

 Future<SplashEntityEntity> fetchPost() async {
    var dio = Dio();
    Response response;
    response = await dio
        .get(WanAndroidApi.OtherCategory, queryParameters: {"type": 1});
//    print(JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)
//        .result
//        .elementAt(0)
//        .album10001000);
    return JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data);
  }

3.2 解析数据详解

3.2.1 官方推荐解析

首先,打开JSON to Dart,JSON to Dar
如下图所示

在这里插入图片描述

Map splashMap = JSON.decode(json);
var splash = new Splash.fromJson(userMap);
print('Howdy, ${splash.code}!');
print('We sent the verification link to ${splash.message}.');

3.2.2 FlutterJsonBeanFactory插件

搜索安卓插件,然后重启工具,这下可以愉快玩耍哦。


在这里插入图片描述

右键会有一个插件,然后让你一件生产对应的dart解析文件哦。


在这里插入图片描述

在这里插入图片描述

解析就是一行代码
JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)

4 异步

当我们访问接口的时候,那么操作肯定需要异步,一边访问请求一边给个进度条,这样体验会更好,异步操作很重要,在flutter中异步很简单,就是靠FutureBuilder和future配合,原生我们都是异步线程,然后刷新ui会切主线程刷新。

Future<SplashEntityEntity> fetchPost() async {
    var dio = Dio();
    Response response;
    response = await dio
        .get(WanAndroidApi.OtherCategory, queryParameters: {"type": 1});
//    print(JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)
//        .result
//        .elementAt(0)
//        .album10001000);
    return JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data);
  }

FutureBuilder<SplashEntityEntity>(
            future: splashBuilder,
            builder: (BuildContext content, AsyncSnapshot async) {
              if (async.connectionState == ConnectionState.done) {
                print("success");
                return getPageView(async.data);
              } else {
                print("loading===");
                return Container(
                  height: 100,
                  width: 100,
                  alignment: Alignment.center,
                  child: CircularProgressIndicator(
                    backgroundColor: Colors.blue,
                    valueColor: AlwaysStoppedAnimation(Colors.blue),
                  ),
                );
                return CircularProgressIndicator(strokeWidth: 1);
              }
            },
          )
  • future:FutureBuilder依赖的Future,通常是一个异步耗时任务。
  • initialData:初始数据,用户设置默认数据。
  • builder:这里对应异步操作的时候对应ui组件,其实都是wiget,注意网络申请要放在初始化中操作,如果有定时器的话,setState更新状态话,就会一直走build.

5 事件讲解

首先这里要讲的事件,在flutter简直无法理解,不知道创造这个人是怎么想的,万物都是Widget,事件怎么能是widget,这真是无限地狱嵌套,代码如下:

5.1 Listener事件

Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.blue,
    width: 300.0,
    height: 150.0,
    child: Text(_event?.toString()??"",style: TextStyle(color: Colors.white)),
  ),
  onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),
  onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),
  onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),
)
事件 介绍
onPointerDown 按下
onPointerUp 抬起时触发
onPointerCancel 取消触摸时触发
onPointerMove 移动时触发

5.2 手势事件

手势这个都是外面包一个,其实这种涉及模式还能理解,需要就外面扩展,不需要就不用写,但是点击事件listener完全不理解呕吐。

GestureDetector(
            child: CircleAvatar(child: Text("A")),
            //垂直方向拖动事件
            onVerticalDragUpdate: (DragUpdateDetails details) {
              setState(() {
                _top += details.delta.dy;
              });
            }
          )
事件 介绍
onTapUp 点击抬起
onTapDown 点击按下
onTapCancel 取消触摸时触发
onTap 点击回调
-------- -----
onDoubleTap 双击
-------- -----
onPanDown 指针已接触屏幕并可能开始移动
onPanStart 指针已经接触屏幕并开始移动
onPanUpdate 与屏幕接触并移动的指针再次移动
onPanEnd 先前与屏幕接触并移动的指针不再与屏幕接触,并且当它停止接触屏幕时以特定速度移动
onPanCancel 先前触发 onPanDown 的指针未完成

6.效果图

在这里插入图片描述
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352