首页

源码搜藏网

首页 > 安卓源码 > 技术博客 >

Flutter完整开发实战详解Dart语言和Flutter基础

创建时间:2019-03-18 22:28  浏览

前言

在如今的 Fultter 大潮下,本系列是让你看完会安心的文章。本系列将完整讲述:如何快速从0开发一个完整的 Flutter APP。同时也会提供一些Flutter的开发细节技巧,并针对开发过程中可能遇到的问题进行填坑。

 

一、基础篇

本篇主要涉及:环境搭建、Dart语言、Flutter的基础。

1、环境搭建

Flutter 的环境搭建十分省心,特别对应 Android 开发者而言,只是在 Android Stuido

上安装插件,并下载flutter Sdk到本地,配置在环境变量即可。其实中文网的搭建Futter开发环境 已经很贴心详细,从平台指引开始安装基本都不会遇到问题。

这里主要是需要注意,因为某些不可抗力的原因,国内的用户需要配置 Flutter 的代理,并且国内用户在搜索 Flutter 第三方包时,也是在 https://pub.flutter-io.cn 内查找,下方是需要配置到环境变量的地址。(ps Android Studio下运行 IOS 也是蛮有意思的(◐‿◑))

///win直接配置到环境编辑即可,mac配置到bash_profile
export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置

2、Dart语言下的Flutter

在跨平台开领域被 JS 一统天下的今天,Dart 语言的出现无疑是一股清流。作为后来者,Dart语言有着不少Java、kotlin 和 JS 的影子,所以对于 Android 原生开发者、前端开发者而言无疑是非常友好的。

官方也提供了包括IOS开发者,React Native 等开发者迁移到 Flutter 上的文档,所以请不要担心,Dart语言不会是你掌握 Flutter 的门槛。甚至作为开发者,就算你不懂 Dart 也可以看着代码摸索。

Come on,下面主要通过对比,简单讲述下 Dart 的一些特性,主要涉及的是 Flutter 下使用。

var 可以定义变量,如 var tag = "666" ,这和 JS 、 Kotlin 等语言类似,同时 Dart 属于动态类型语言,支持闭包。

Dart 中 number 类型分为 int 和 double ,其中 java 中的 long 对应的也是 Dart 中的 int 类型。Dart 中没有 float 类型。

Dart 下只有 bool 型可以用于 if 等判断,不同于 JS 这种使用方式是不合法的 var g = "null"; if(g){} 。

DART中,switch 支持 String 类型。

Dart 不需要给变量设置 setter getter 方法, 这和 kotlin 等类似。Dart 中所有的基础类型、类等都继承 Object ,默认值是 NULL, 自带 getter 和 setter ,而如果是 final 或者 const 的话,那么它只有一个 getter 方法。

Dart 中 final 和 const 表示常量,比如 final name = 'GSY'; const value= 1000000; 同时 static const 组合代表了静态常量。其中 const 的值在编译期确定,final 的值要到编译时才确定。(ps Flutter 在 Release 下是 AOT 模式。)

Dart 下的数值,在作为字符串使用时,是需要显式指定的。比如:int i = 0; print("aaaa" + i); 这样并不支持,需要 print("aaaa" + i.toString()); 这样使用。这和 Java 与 JS 存在差异。所以在使用动态类型时,需要注意不要把 number 类型当做 String 使用。

DART 中数组等于列表,所以 var list = []; 和 List list = new List() 可以简单看做一样。

Dart 下 ?? 、??= 属于操作符,如: AA ?? "999" 表示如果 AA 为空,返回999;AA ??= "999" 表示如果 AA 为空,给 AA 设置成 999。

Dart 方法可以设置 参数默认值 和 指定名称 。比如: getDetail(Sting userName, reposName, {branch = "master"}){} 方法,这里 branch 不设置的话,默认是 “master” 。参数类型 可以指定或者不指定。调用效果: getRepositoryDetailDao(“aaa", "bbbb", branch: "dev");

Dart 不像 Java ,没有关键词 public 、private 等修饰符,_下横向直接代表 private ,但是有 @protected 注解。

Dart 中多构造函数,可以通过如下代码实现的。默认构造方法只能有一个,而通过Model.empty() 方法可以创建一个空参数的类,其实方法名称随你喜欢。而变量初始化值时,只需要通过 this.name 在构造方法中指定即可:

class ModelA {
 String name;
 String tag;
 
 //默认构造方法,赋值给name和tag
 ModelA(this.name, this.tag);
 //返回一个空的ModelA
 ModelA.empty();
 
 //返回一个设置了name的ModelA
 ModelA.forName(this.name);
}

Flutter 中支持 async/await 。这一点和 ES7 很像,如下代码所示,只是定义的位置不同。同时异步操作也和 ES6 中的Promise 很像,只是 Flutter 中返回的是 Future 对象,通过 then 可以执行下一步。如果返回的还是 Future 便可以 then().then.() 的流式操作了 。

 ///模拟等待两秒,返回OK
 request() async {
 await Future.delayed(Duration(seconds: 1));
 return "ok!";
 }
 ///得到"ok!"后,将"ok!"修改为"ok from request"
 doSomeThing() async {
 String data = await request();
 data = "ok from request";
 return data;
 }
 ///打印结果
 renderSome() {
 doSomeThing().then((value) {
 print(value);
 ///输出ok from request
 });
 }

Flutter 中 setState 很有 React Native 的既视感,Flutter 中也是通过 state 跨帧实现管理数据状态的,这个后面会详细讲到。

Flutter 中一切皆 Widget 呈现,通过 build方法返回 Widget,这也是和 React Native 中,通过 render 函数返回需要渲染的 component 一样的模式。

3、Flutter Widget

在 Flutter 中,一切的显示都是 Widget 。Widget 是一切的基础,作为响应式的渲染,属于 MVVM 的实现机制。我们可以通过修改数据,再用setState 设置数据,Flutter 会自动通过绑定的数据更新 Widget 。所以你需要做的就是实现 Widget 界面,并且和数据绑定起来。

Widget 分为 有状态 和 无状态 两种,在 Flutter 中每个页面都是一帧。无状态就是保持在那一帧。而有状态的 Widget 当数据更新时,其实是绘制了新的 Widget,只是 State 实现了跨帧的数据同步保存。

这里有个小 Tip ,当代码框里输入 stl 的时候,可以自动弹出创建无状态控件的模板选项,而输入 stf 的时,就会弹出创建有状态 Widget 的模板选项。

代码格式化的时候,括号内外的逗号都会影响格式化时换行的位置。

如果觉得默认换行的线太短,可以在设置-Editor-Code Style-Dart-Wrapping and Braces-Hard wrap at 设置你接受的数值。

3.1、无状态StatelessWidget

直接进入主题,下方代码是无状态 Widget 的简单实现。

继承 StatelessWidget,通过 build 方法返回一个布局好的控件。可能现在你还对 Flutter 的内置控件不熟悉,but Don't worry , take is easy ,后面我们就会详细介绍。这里你只需要知道,一个无状态的 Widget 就是这么简单。 

Widget 和 Widget 之间通过 child: 进行嵌套。其中有的 Widget 只能有一个 child,比如下方的 Container ;有的 Widget 可以多个 child ,也就是children:,比如` Colum 布局。下方代码便是 Container Widget 嵌套了 Text Widget。

import 'package:flutter/material.dart';
class DEMOWidget extends StatelessWidget {
 final String text;
 //数据可以通过构造方法传递进来
 DEMOWidget(this.text);
 @override
 Widget build(BuildContext context) {
 //这里返回你需要的控件
 //这里末尾有没有的逗号,对于格式化代码而已是不一样的。
 return Container(
 //白色背景
 color: Colors.white,
 //Dart语法中,?? 表示如果text为空,就返回尾号后的内容。
 child: Text(text ?? "这就是无状态DMEO"),
 );
 }
}

3.2、有状态StatefulWidget

继续直插主题,如下代码,是有状态的widget的简单实现。

你需要创建管理的是主要是 State , 通过 State 的 build 方法去构建控件。在 State 中,你可以动态改变数据,这类似 MVVM 实现,在 setState 之后,改变的数据会触发 Widget 重新构建刷新。而下方代码中,是通过延两秒之后,让文本显示为 "这就变了数值"

如下代码还可以看出,State 中主要的声明周期有 :

看到没,Flutter 其实就是这么简单!你的关注点只要在:创建你的 StatelessWidget 或者 StatefulWidget 而已。你需要的就是在 build 中堆积你的布局,然后把数据添加到 Widget 中,最后通过 setState 改变数据,从而实现画面变化。

import 'dart:async';
import 'package:flutter/material.dart';
class DemoStateWidget extends StatefulWidget {
 final String text;
 ////通过构造方法传值
 DemoStateWidget(this.text);
 ///主要是负责创建state
 @override
 _DemoStateWidgetState createState() => _DemoStateWidgetState(text);
}
class _DemoStateWidgetState extends State<DemoStateWidget> {
 String text;
 _DemoStateWidgetState(this.text);
 
 @override
 void initState() {
 ///初始化,这个函数在生命周期中只调用一次
 super.initState();
 ///定时2秒
 new Future.delayed(const Duration(seconds: 1), () {
 setState(() {
 text = "这就变了数值";
 });
 });
 }
 @override
 void dispose() {
 ///销毁
 super.dispose();
 }
 @override
 void didChangeDependencies() {
 ///在initState之后调 Called when a dependency of this [State] object changes.
 super.didChangeDependencies();
 }
 @override
 Widget build(BuildContext context) {
 return Container(
 child: Text(text ?? "这就是有状态DMEO"),
 );
 }
}

4、Flutter 布局

Flutter 中拥有需要将近30种内置的 布局Widget,其中常用有 Container、Padding、Center、Flex、Stack、Row、Colum、ListView 等,下面简单讲解它们的特性和使用。

Flutter完整开发实战详解Dart语言和Flutter基础

 

 new Container(
 ///四周10大小的maring
 margin: EdgeInsets.all(10.0),
 height: 120.0,
 width: 500.0,
 ///透明黑色遮罩
 decoration: new BoxDecoration(
 ///弧度为4.0
 borderRadius: BorderRadius.all(Radius.circular(4.0)),
 ///设置了decoration的color,就不能设置Container的color。
 color: Colors.black,
 ///边框
 border: new Border.all(color: Color(GSYColors.subTextColor), width: 0.3)),
 child:new Text("666666"));
//主轴方向,Colum的竖向、Row我的横向
mainAxisAlignment: MainAxisAlignment.start, 
//默认是最大充满、还是根据child显示最小大小
mainAxisSize: MainAxisSize.max,
//副轴方向,Colum的横向、Row我的竖向
crossAxisAlignment :CrossAxisAlignment.center,
 new Column(
 ///主轴居中,即是竖直向居中
 mainAxisAlignment: MainAxisAlignment.center,
 ///大小按照最小显示
 mainAxisSize : MainAxisSize.min,
 ///横向也居中
 crossAxisAlignment : CrossAxisAlignment.center,
 children: <Widget>[
 ///flex默认为1
 new Expanded(child: new Text("1111"), flex: 2,),
 new Expanded(child: new Text("2222")),
 ],
 );

接下来我们来写一个复杂一些的控件。首先我们创建一个私有方法_getBottomItem,返回一个 Expanded Widget,因为后面我们需要将这个方法返回的 Widget 在 Row 下平均充满。

如代码中注释,布局内主要是现实一个居中的Icon图标和文本,中间间隔5.0的 padding:

 ///返回一个居中带图标和文本的Item
 _getBottomItem(IconData icon, String text) {
 ///充满 Row 横向的布局
 return new Expanded(
 flex: 1,
 ///居中显示
 child: new Center(
 ///横向布局
 child: new Row(
 ///主轴居中,即是横向居中
 mainAxisAlignment: MainAxisAlignment.center,
 ///大小按照最大充满
 mainAxisSize : MainAxisSize.max,
 ///竖向也居中
 crossAxisAlignment : CrossAxisAlignment.center,
 children: <Widget>[
 ///一个图标,大小16.0,灰色
 new Icon(
 icon,
 size: 16.0,
 color: Colors.grey,
 ),
 ///间隔
 new Padding(padding: new EdgeInsets.only(left:5.0)),
 ///显示文本
 new Text(
 text,
 //设置字体样式:颜色灰色,字体大小14.0
 style: new TextStyle(color: Colors.grey, fontSize: 14.0),
 //超过的省略为...显示
 overflow: TextOverflow.ellipsis,
 //最长一行
 maxLines: 1,
 ),
 ],
 ),
 ),
 );
 }
Flutter完整开发实战详解Dart语言和Flutter基础

 

接着我们把上方的方法,放到新的布局里。如下流程和代码:

 @override
 Widget build(BuildContext context) {
 return new Container(
 ///卡片包装
 child: new Card(
 ///增加点击效果
 child: new FlatButton(
 onPressed: (){print("点击了哦");},
 child: new Padding(
 padding: new EdgeInsets.only(left: 0.0, top: 10.0, right: 10.0, bottom: 10.0),
 child: new Column(
 mainAxisSize: MainAxisSize.min,
 children: <Widget>[
 ///文本描述
 new Container(
 child: new Text(
 "这是一点描述",
 style: TextStyle(
 color: Color(GSYColors.subTextColor),
 fontSize: 14.0,
 ),
 ///最长三行,超过 ... 显示
 maxLines: 3,
 overflow: TextOverflow.ellipsis,
 ),
 margin: new EdgeInsets.only(top: 6.0, bottom: 2.0),
 alignment: Alignment.topLeft),
 new Padding(padding: EdgeInsets.all(10.0)),
 ///三个平均分配的横向图标文字
 new Row(
 crossAxisAlignment: CrossAxisAlignment.start,
 children: <Widget>[
 _getBottomItem(Icons.star, "1000"),
 _getBottomItem(Icons.link, "1000"),
 _getBottomItem(Icons.subject, "1000"),
 ],
 ),
 ],
 ),
 ))),
 );
 }
Flutter完整开发实战详解Dart语言和Flutter基础

 

Flutter 中,你的布局很多时候就是这么一层一层嵌套出来的,当然还有其他更高级的布局方式,这里就先不展开了。

5、Flutter 页面

Flutter 中除了布局的 Widget,还有交互显示的 Widget 和完整页面呈现的Widget。其中常见的有 MaterialApp、Scaffold、Appbar、Text、Image、FlatButton等。下面简单介绍这些 Wdiget,并完成一个页面。

Flutter完整开发实战详解Dart语言和Flutter基础

 

import 'package:flutter/material.dart';
import 'package:gsy_github_app_flutter/test/DemoItem.dart';
class DemoPage extends StatefulWidget {
 @override
 _DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
 @override
 Widget build(BuildContext context) {
 ///一个页面的开始
 ///如果是新页面,会自带返回按键
 return new Scaffold(
 ///背景样式
 backgroundColor: Colors.blue,
 ///标题栏,当然不仅仅是标题栏
 appBar: new AppBar(
 ///这个title是一个Widget
 title: new Text("Title"),
 ),
 ///正式的页面开始
 ///一个ListView,20个Item
 body: new ListView.builder(
 itemBuilder: (context, index) {
 return new DemoItem();
 },
 itemCount: 20,
 ),
 );
 }
}

最后我们创建一个StatelessWidget作为入口文件,实现一个MaterialApp将上方的DemoPage设置为home页面,通过main入口执行页面。

import 'package:flutter/material.dart';
import 'package:gsy_github_app_flutter/test/DemoPage.dart';
void main() {
 runApp(new DemoApp());
}
class DemoApp extends StatelessWidget {
 DemoApp({Key key}) : super(key: key);
 @override
 Widget build(BuildContext context) {
 return new MaterialApp(home: DemoPage());
 }
}
Flutter完整开发实战详解Dart语言和Flutter基础

 

好吧,第一部分终于完了,这里主要讲解都是一些简单基础的东西,适合安利入坑。

上一篇:Android开发使用sqlite实现自动缓存json数据
下一篇:Android 关于虹软人脸识别SDK引擎使用总结

相关内容

热门推荐