flutter小demo为期一个月的练手

哈喽大家好,一个月没发文,那是因为我沉浸在学习的海洋中,没错,今天要说的就是关于flutter的学习总结,这一篇总结来自一名Android开发的视角。

转载注明出处,CSDN第六篇

大部分的基础我都是通过技术胖的文章和视频来学习,上手较快,当然flutter中文网也是个不错的选择。

我是一名Android小开发,在一家外包工作四年之后,跳到这家大公司,但疫情之下即使大公司也举步维艰,所以不得不多学习充实自己,这样才避免未来被淘汰。这里的经理还是一位很有见地的人,各种安排都是为了同事的未来发展,经过他的指点我上个月的计划都安排给了flutter。

好了话不多说,开始正文。

flutter之初印象

由于我是Android开发,使用Android studio可以更快速的搭建flutter的环境,我直接下载SDK,配置环境变量,然后在studio里安装插件,就没问题直接开始撸代码了。
写过小程序的我感觉看flutter代码毫无压力,完全一样的格式和调用关系,唯一不同的就是组件之间的嵌套,一层又一层,我也试过用in()的方式来解决嵌套,奈何网上没有好用的第三方,作为一个菜鸟只能老老实实的嵌套。

flutter基础理解

其实什么语言不重要,重要的是对于原理的理解,比如界面如何计算每个组件的位置,每个组件自身的属性,如何改变某些变量,和服务器或者用户交互等等。
首先最基础的一句就是,万物都是widget!,就代表任何页面都可以相互嵌套调用。

1、flutter文件

flutter的项目中主要有三个文件夹,android,ios,lib。而flutter的代码都是在lib里面的。

2、flutter代码

flutter的代码风格仿佛是小程序代码和js的结合体。
创建变量使用var相当于Android的Object,当然也可以固定类型int,String,List等等。
当变量发生改变的时候,就可以嵌套setState来刷新页面,但是最好先判断mounted状态,防止页面未打开而刷新造成报错。
当耗时操作的时候需要进行异步操作,方法后添加async,耗时操作前加await

3、原生对接

fulutter提供了MethodChannel来对接原生的方法,其实大部分第三方库也都是对接安卓和苹果的原生代码实现,原理就是定义同样的方法名,我猜应该是通过反射去调用。
在安卓的原生里需要注册一下GeneratedPluginRegistrant,同时继承FlutterActivity

flutter布局理解

1、入口

flutter打开app的入口是main方法,在该方法返回一个MaterialApp的类,相当于Android 的Application,其中的属性有:
title:应用的名称
theme:应用的主题
home:应用的启动页面,返回一个Widget
routes:应用的路由,相当于Android的AndroidManifest中的配置,其实就是给每个页面起一个别名,方便跳转的时候调用。
键值对形式,如:’/home’: (BuildContext context) => HomePage()
若没有配置路由则使用 Navigator.push()来跳转, Navigator.pop()来返回上一页

2、基础组件

最基础的就是StatelessWidget和StatefullWidget这两种组件,就像是窗户的骨架,StatelessWidget是固定不动的静态的那扇,StatefullWidget是左右推动的动态的那扇,我作为简单粗暴的小开发,我直接无脑使用StatefulWidget永远可以推动的动态窗户。
而继承StatefulWidget之后,就是必须实现的方法createState(),在该方法中必须返回一个继承State的类出来。
State中除了包含常规的构造方法初始化方法,也有自己一套生命周期,还有一个必须实现的方法build
构造方法用来传参,初始化用来初始化数据和控制器,生命周期控制页面状态,而要搭建我们的页面,就要在build方法中返回一个widget

3、组件

Scaffold:若是一个新页面,多半是要用这个组件开始,它代表的占满整个屏幕的一个完整页面,包含标题栏,主体和底部导航。
appBar:标题栏,系统自动计算高度,直接使用AppBar这个组件即可
body:主体页面,若是首页可使用TabBarView,可以对应导航的点击事件去切换子页面。
bottomNavigationBar:底部导航栏,我这里直接使用TabBar这个组件,可以和TabBarView绑定同一个controller,这样实现一个项目的首页太简单了。
TabBarView:相当于Android的viewpager,但是和tabber绑定特别好用,除了绑定TabBar的controller外,在children中返回一个Widget的数组即可。
TabBar:导航类的组件,可定义选中和未选中的颜色,在children中返回一个Tab的数组。
Tab:包含图标icon和文字text两个属性。
Container:这个组件Android中没发现类似,反而比较像是Html中的div标签,需要一些外边距margin,内边距padding,居中alignment,边框decoration和背景颜色color的时候就使用这个组件,反正有事没事就加一层Container,但是要注意边框decoration和背景颜色color不能同时使用
Row:横向布局,mainAxisAlignment和crossAxisAlignment来控制子组件的居中
Column:纵向布局,mainAxisAlignment和crossAxisAlignment来控制子组件的居中。这里将横向和纵向合起来说一下,因为在Android中是LinearLayout,其他并没有什么区别,若要占满宽度或者高度,只需要在children中的使用Expanded即可。
Stack:相对布局,相当于Android的FrameLayout
这些都是布局类的组件,控件类的组件就比较容易了,图片Image,文字Text,输入框TextField
InkWell:点击组件,实现onTap方法即可
RefreshIndicator:上拉刷新,下拉加载。onRefresh可实现刷新,controller的监听判断在滑动底部实现加载。
ListView.builder:为什么不是ListView呢,因为flutter的ListView就和Column没什么区别,只是可以滚动了。而用这个组件,可以实现Andorid的ListView的功能,设置控制器controller,条目数量itemCount,条目布局itemBuilder
SingleChildScrollView:相当于Android的ScrollView

4、配置

pubspec.yaml是flutter的app级别的配置文件,除了配置版本信息之外,还包括第三方库的引用,本地资源的引用等等

1、第三方

大部分都是直接在网上寻找第三方,非常感谢网上那么多开路的大神,为flutter提供了如此多的代码库,以下列出我使用到的库:

dio: ^3.0.9	//网络请求
shared_preferences: ^0.5.3+1	//数据保存
flutter_swiper: ^1.0.6	//轮播图
 video_player: ^0.10.1+6
  chewie: ^0.9.8+1	//视频播放器
  audioplayers: ^0.13.1	//音频播放器
  flutter_local_notifications: ^0.9.1+2	//通知
2、本地资源

我这里仅仅用到了本地图标,每一张图片都必须配置,可以说十分不方便,我是在
lib同级的地方创建了一个images的文件夹来使用,配置一下信息

 	- images/557222.png
    - images/557249.png
    //下面是使用演示
    Image.asset( 'images/557222.png',
                       width: 25,
                       height: 25,
                       )
4、功能

这里会写出我这一个月的时间到底用flutter实现了什么功能,用什么方法实现的,也算是写出一点学(百)习(度)经验。

1、沉浸式

在main方法中添加一下代码:

void main() {
  runApp(MyApp());
  if (Platform.isAndroid) {
    // 以下两行 设置android状态栏为透明的沉浸。
    // 写在组件渲染之后,是为了在渲染后进行set赋值,覆盖状态栏,
   //  写在渲染之前MaterialApp组件会覆盖掉这个值。
    SystemUiOverlayStyle systemUiOverlayStyle =
        SystemUiOverlayStyle(statusBarColor: Colors.transparent);
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }
}

这样的话只需要设置当前页面的背景颜色就可以实现沉浸式,但是还需要控制状态栏的字体颜色,在每个页面的根布局Scaffold的外层嵌套:

AnnotatedRegion<SystemUiOverlayStyle>(
      value: isdark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark,
       child: Scaffold(
       )
 )
2、网络请求

flutter中的网络请求一般在initState中去初始化,当然根据项目要求灵活调整,使用多中代码库后,发现还是dio比较好用。
这里要特别说明的一点是,赋值的时候使用setState嵌套刷新页面,外面要判断页面是否打开mounted,。

 void getList() async {
         try {
        shows.clear();
        var url =
            'https://api.yunxuekeji.cn/yunxue_app_api/content/getColumn/1/73334';
        var response = await Dio().get(url);
        if (response.statusCode == HttpStatus.OK) {
          var data = response.data;
          if (data['code'] == 200) {
            var json = data['body'];
            if (mounted) {
              setState(() {
                list.addAll(new List.from(json['result']));
              });
          } else {
            Toast.toast(context, msg: data['message']);
          }
        } else {
          Toast.toast(context, msg: "服务器异常");
        }
      } on Exception catch (e) {
        Toast.toast(context, msg: "当前网络质量不佳");
      }
    }
  }
3、Toast和弹窗

flutter中的OverlayEntry可以将布局插入到当前页面之上,所以可以用来实现Toast和弹窗功能。实现的方法一样,唯一的区别是Toast要定时去关闭,而弹窗等待用户的反馈去关闭。

	//这里创建一个OverlayEntry
  OverlayEntry overlay = OverlayEntry(
          builder: (BuildContext context) => Positioned(
                //top值,默认在屏幕下方
                top: MediaQuery.of(context).size.height * 3 / 4,
                child: Material(),
              ));
      //插入context所在的页面
      Overlay.of(context).insert(overlay);
      //重绘方法,达到刷新的效果
      overlay.markNeedsBuild();
      //关闭方法
       overlay.remove();
3、音频播放器

音频播放器是在第三方 audioplayers: ^0.13.1和弹窗的结合实现的,这里直接展示相关代码:

import 'package:audioplayers/audioplayers.dart';
AudioPlayer  player;
AudioUtils(Function function) {
    player = new AudioPlayer();
    player.onNotificationPlayerStateChanged.listen(function);
  }
  //下面是相关操作,result=1是成功
 // int result = await player.play(url);
 // int result = await player.pause();
  //int result = await player.resume();
  //int result = await player.stop();
 // int result = await player.release();
 //int result = await player.seek(new Duration(milliseconds: mill));
  //使用示例,要使用异步
   play(context, url) async {
    int result = await player.play(url);
    if (result == 1) {
      AudioUi.play(context);//刷新ui到播放状态
      isPlay = true;
      print('播放成功');
    } else {
      isPlay = false;
      print('播放失败');
    }
    AudioUi.refresh();//根据isPlay刷新ui
  }

想必有人好奇AudioUi中怎么写的:

class AudioUi {
static BuildContext context;
static OverlayEntry overlay;
static play(BuildContext c) {
    context = c;
    if (overlay == null) {
      overlay = OverlayEntry(
          builder: (BuildContext context) => Positioned(
                //top值,默认在屏幕下方
                top: MediaQuery.of(context).size.height * 3 / 4,
                child: Material(
                  color: Colors.transparent,
                  child: Container(
                  width: MediaQuery.of(context).size.width - 40,
                  color: Color(0x11000000),
                  child: Padding(
                      padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
                      child: InkWell(
                            onTap: () {
                              if (MyHomePage.audioUtils != null) {
                                MyHomePage.audioUtils.release();
                              }
                            },
                            child: Container(
                              padding: EdgeInsets.all(10.0),
                              child: Image.asset(
                                "images/557249.png",
                                width: 30,
                                height: 30,
                              ),
                            ),
                          ),
                        ),
                    ),
                  ),
                ),
              ));
      Overlay.of(context).insert(overlay);
    } else {
      overlay.markNeedsBuild();
    }
     static refresh() {
    if (overlay != null) {
      overlay.markNeedsBuild();
    }
  }

  static close() {
    if (overlay != null) {
      overlay.remove();
      overlay = null;
    }
  }
  }
4、数据保存

flutter的sharedPreferences和Android略有不同,就是flutter的sharedPreferences获取数据的时候竟然是异步的,这样就不在一个线程了,获取结果很麻烦。而经过我的改进,用静态的方式改为和Android获取一样的效果。
注意:需要在刚开始初始化一下

class SharedpUtil {
  static SharedPreferences sharedPreferences;

  static void init() async {
    if (sharedPreferences == null) {
      sharedPreferences = await SharedPreferences.getInstance();
    }
  }
   static void putString(String key, String value) async {
    if (sharedPreferences == null) {
      return;
    }
    sharedPreferences.setString(key, value);
  }
   static String getString(String key) {
    if (sharedPreferences == null) {
      return "";
    }
    String get = sharedPreferences.getString(key);
    return get;
  }  }
3、其他功能

虽然我还实现了轮播图,播放器和通知栏,但是其中的技术含量还是很低的,没有经过自己的改进,所以就不贴出来了,各位去找一下类似的文章即可,我也是从那些文章里搬过来用的。

flutter个人总结

为期一个月,略微实现了一个小demo,流畅度也不错,但是打包没有成功,需要翻墙;也没有试过IOS适配如何。
虽说flutter的发展越来越好,但2020年一直跟着别人后面走的小开发来说,还是不值得投入太多,毕竟咱不是领头的大牛,现在可用的好用的第三方还是有些少,比如我这个demo中关于直播的即时通讯的相关几乎没有。
我的观点就是,能学会尽早学会,语法也不是很难,当flutter能一统前端的时候,就可以直接出山了。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值