Flutter实战 从头撸一个「孤岛」APP(No.4、流行、点赞、动画)

5,283 阅读6分钟

前言

当你看到这篇文章的时候,已经是2020的某一天,从此以后再无2019。不知道这一年来你收获了多少呢,我也十分感谢你能打开这份年末我予你最好的礼物,不得不说你是个上进的人

首先值得开心的有两件事

  1. 我给自己添了个大件 这看文章,一屏就能放下,不用滚动

    lrgujO.md.jpg
    没错这款就是 BENQ 明基的BL2480T 我怎么感觉我像个开箱的UP,周末没事,我还看了看掘金大佬们的分享,我裂开了

  2. 第二件事,就是我在 两台设备上都装了图床小工具

这个很好用,对于时长写文章的话,能提高些效率,Mac 上装的是uPic

其实自己想写点东西,又中断,主要原因是自己的那台小Mac 是12-13年的机子,Flutter 应用是打不开的,很卡很卡,期望自己在2020年能DIY一台,专门写文章,手动加到 一位前端弱者的2019年总结|掘金年度征文 这里

上期回顾

言归正传(不知道为什么总喜欢扯点别的),就像写的一篇很实用的flutter状态管理相关的 文章 好像并没有多少阅读量,扯的太远,不过你放心,这篇依然是很干

读读评论

  • 辉太郎2019

    • 其中他是这样说的:

      有个地方变了,search_widget的包名变了,建议更新下 插件官方说明

      https://pub.dev/packages/search_widget
      pub.dev/packages/se… 引入方式 import 'package:search_widget/search_widget.dart';

    当时写的一个搜索模块,是借助第三方的包,不过在实际开发中还是尽量手写比较基础的模块,比如AppBar搜索,或者页面中的搜索,正是在搜索的分享中,有的地方有点错误,谢谢指正

  • yj0099

    • 其中他是这样说的:

      赞,最近刚开始看flutter,dart真的可以。但是flutter的套娃写法真的有点把我劝退了

      首先还是很感谢对自己的肯定,有很多人接触flutter 的时候,总是说嵌套地狱,怕了怕了,笔者倒没有觉得有这个问题啊,

      打个比方,我们要写一个布局,要换一种思维来写,从小部件来出发

       // 我是第一行
        Widget _firRow() => Container(
                child: Row(
              children: <Widget>[
                Text('我是第一行'),
              ],
            ));
      
        // 我是第二行
        Widget _SecRow() => Container(
                child: Row(
              children: <Widget>[
                Text('我是第er行'),
              ],
            ));
      
        // 我是第三行
        Widget _ThrRow() => Container(
                child: Row(
              children: <Widget>[
                Text('我是第er行'),
              ],
            ));
      
        // 我是三行的汇总
        Widget _all() => Container(
              child: Column(
                children: <Widget>[
                  // 第一行
                  _firRow(),
                  // 第二行
                  _SecRow(),
                  // 第三行
                  _ThrRow()
                ],
              ),
            );
      

这也是我实际开发中的模式,所以也没觉得有嵌套的问题,反而觉得写起来很舒服,我还特地的 看了下,关注者,

不知你是不是101

期待效果

lrgNgf.md.png

实际效果

开始操练

遗留问题

  • 首先,咱们克隆下仓库之后,发现,代码是不完全的,只有4 commits 这应该是我自己的原因 没有把代码同步上去

  • 第二个问题是克隆下来代码,run 了一下是报错误的

lrcqBQ.md.png

遇到问题莫要慌张,本章节,我会整理一下项目,再clone 代码就不会有问题了,看过前几篇文章的应该知道,在我创建项目的时候,Flutter SDK 还是1.9+版本,那么就在前几天不久前,它翘首以盼的更新了,大体没有让开发者失望,包括HOT UI 等,网上有大佬分享出一些新的东西,这段路就不多做唠

  • 当前项目的flutter 版本

  • 当前直接克隆代码就可

Dio数据再分析

在上一篇的时候

Flutter实战 从头撸一个「孤岛」APP(No.3、书单、搜索框、Dio初探)

我们简单的分析了一下Dio的数据,此时不要慌,这里先埋下一个伏笔,那就是Json to Dart, 接触过flutter 开发的可能已经知道了那就是很犯迷糊的一步,把json数据转为Model类,至于怎么才能一步到位,傻瓜式转换,拿来直接用,那像Web 开发中,直接用后台返回json 不行吗,为什么还要转化,咱们留到下一段旅程

流行、点赞

本篇咱们就来先看一下首页(流行)模块的点赞效果,首先还是先初始化一行(头部)

 body: SingleChildScrollView(
          child: Flex(
            direction: Axis.vertical,
            children: <Widget>[
              Container(
                child: _like(),
              )
            ],
          ),
        )
  // 点赞 第一行
  Widget _like() => Container(
        child: Row(
          children: <Widget>[
            Container(
              width: width750,
              height: height100,
              decoration: BoxDecoration(
                  border: Border.all(width: 1, color: Colors.red)),
              child: Text('头部'),
            )
          ],
        ),
      );

这样是不是就不怎么嵌套了呢,就像开头提及的那样,然后在widgets文件夹下新建一个点赞的 文件,用来放点赞的效果

使用编辑器的快捷方式来新建代码段

这里你可以去翻下我的其他文章,也有提到一些常用的快捷键

动画Animation

控制器

控制器在应用中还是很常见的,包括像文本输入、还有滚动部件,那么动画当然也不例外

  • 初始化控制器

     AnimationController _animationController; // 
    
  • 初始化控制器

    void initState() {
        super.initState();
        _animationController = AnimationController(
          
        );
      }
    

那么有个问题 我们怎么知道有什么值呢

 AnimationController({
    double value,
    this.duration, // 时长
    this.reverseDuration,
    this.debugLabel,
    this.lowerBound = 0.0, //  开始的值
    this.upperBound = 1.0, // 结束的值
    this.animationBehavior = AnimationBehavior.normal,
    @required TickerProvider vsync,
  }) : assert(lowerBound != null),
       assert(upperBound != null),
       assert(upperBound >= lowerBound),
       assert(vsync != null),
       _direction = _AnimationDirection.forward {
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value ?? lowerBound);
  }

以上我们可以简单的看下

  • 开始播放动画

    _animationController.forward();
    
    
    
  • 销毁无用的控制器

    @override
      void dispose() {
        super.dispose();
        _animationController.dispose();
      }
    

完整代码

import 'package:flutter/material.dart';
import 'package:lsolated_island_app/utils/global.dart';

class WidgetLike extends StatefulWidget {
  WidgetLike({Key key}) : super(key: key);

  @override
  _WidgetLikeState createState() => _WidgetLikeState();
}

class _WidgetLikeState extends State<WidgetLike> with TickerProviderStateMixin {
  AnimationController _animationController; // 动画控制器
  Animation _animationSize; // 大小
  Animation _animationColor; // 颜色
  CurvedAnimation _curvedAnimation;

  // 初始化
  @override
  void initState() {
    super.initState();

    _animationController = AnimationController(
      // value: 20.0, //
      // lowerBound: 20.0,
      // upperBound: 100.0,
      duration: Duration(milliseconds: 1000), // 持续的时长
      vsync: this, // 每一帧的反应 一秒60 this 当前对象
    );

    _animationSize =
        Tween(begin: 30.0, end: 100.0).animate(_animationController);

    // // 监听
    _animationController.addListener(() {
      // print('${_animationController.value}');
      setState(() {});
    });
    // 开始播放动画
    // _animationController.forward();
    // // 设置动画的范围

    _animationColor = ColorTween(begin: Colors.red, end: Colors.red[900])
        .animate(_animationController);

    _curvedAnimation = CurvedAnimation(
        parent: _animationController, curve: Curves.bounceOut); // 动画曲线

    // // 监听状态
    _animationController.addStatusListener((AnimationStatus status) {});
  }

  @override
  void dispose() {
    super.dispose();
    _animationController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Column(
      children: <Widget>[
        Container(
          child: Text('${_animationController.value}'),
        ),
        Container(
          child: IconButton(
            iconSize: _animationSize.value,
            color: _animationColor.value,
            onPressed: () {
              print('点击了');
              // _animationController.forward();

              switch (_animationController.status) {
                case AnimationStatus.completed:
                  _animationController.reverse();
                  break;
                default:
                  _animationController.forward();
              }
            },
            icon: Icon(Icons.favorite),
          ),
        )
      ],
    ));
  }
}

第三方辅助

当然了,通过如上我们会对动画有一定的理解,不过像这这种常见的效果,一般会有些对Flutter积淀的大佬们写成共用的第三方部件,来提供开发者使用,并不断的更新维护,但是这个也是跟情况而定,一般移动的应用都会牵扯到包的大小,一些简单的效果,可以尝试自己来写,那么接下来,来pub.dev 上一个开放的轮子

社区概览

002.png

那我们怎么使用呢 ?引入包之后,像这样使用一下就可

return Scaffold(
        appBar: AppBar(),
        body: Container(
          child: Center(
            child: LikeButton(),
          ),
        ));

代码同步至Demo 点赞Demo

特别感谢