上周个人一位微信好友问我有没有学Flutter
,我回答说还没真正学,他说应该要接触一下。对于新技术的诞生,我始终保持敬畏之心,和另外一位大学舍友聊了当时如何入坑Android
的经历,才发现本身的学习方式和路线有不少的问题,知识点很零乱,知识没有系统化,很少说了,后面学习新的知识必定要从“碎片化”到“总体化”。2018年2月,在世界移动大会上,Google发布了Flutter的第一个beta版本,2018年6月11日发布首个预览版,在2018年12月05日北京时间凌晨1点45分,在Flutter Live上,谷歌Flutter团队推出Flutter1.0,Flutter1.0版本是UI工具包的第一个稳定版本,在2019年2月27日世界移动通讯大会上Google推出1.2版本,带来全新的Web开发工具。另外今日头条团队即将开源让Flutter真正支持View级别的混合开发(上层Flutter Framework引入Widget/LayerTree等概念本身实现了界面描述框架,下层Flutter Engine把LayerTree用OpenGL渲染成用户界面),闲鱼团队也开源了基于 Redux数据管理的组装式Flutter应用框架。什么是Flutter呢?Flutter是一个跨平台的免费开源的移动UI框架,是Google的移动应用SDK,用于在极短期内在iOS和Android平台上建立高质量的原生体验,简而言之就是在iOS下和Android下共用一套代码,一套代码就能在两个操做系统下运行,其官方编程语言是是Dart,学习这门语言很快就上手。Flutter提供不少丰富的UI组件库,开发者能够快速开发出灵活的UI界面,另外Flutter已经加入Material Design组件你们庭中,也就是说Flutter可使用Material Theming和Material 组件,能够相信将来会有更多的创意设计UI风格会涌现。java
class CounterState extends State<Counter> {
int counter = 0;
void increment() {
// Tells the Flutter framework that state has changed,
// so the framework can run build() and update the display.
setState(() {
counter++;
});
}
Widget build(BuildContext context) {
// This method is rerun every time setState is called.
// The Flutter framework has been optimized to make rerunning
// build methods fast, so that you can just rebuild anything that
// needs updating rather than having to individually change
// instances of widgets.
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: increment,
child: new Text('Increment'),
),
new Text('Count: $counter'),
],
);
}
}
复制代码
Future<Null> getBatteryLevel() async {
var batteryLevel = 'unknown';
try {
int result = await methodChannel.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level: $result%';
} on PlatformException {
batteryLevel = 'Failed to get battery level.';
}
setState(() {
_batteryLevel = batteryLevel;
});
}
复制代码
2019年一月底,Flutter团队公布了2019年Flutter的产品线,为如下几点肯定了明确的计划:android
web
应用。 而且Flutte UX研究团队会按期根据用户反馈来助力打造更优的Flutter,根据用户反馈来调整开发重点,可见Flutter UX团队的用心和付出程度,今年拭目以待。Flutter框架从到下包含三部分:函数式响应的Framework(Dart),Engine(C++),Embedder(Platform Specific)。下面直接上图: ios
务必电脑安装git,本文是在Windows环境下配置的,MAC下配置环境流程是一致的。首先要获取Flutter SDK,使用git去克隆仓库而后添加Flutter工具到本身的环境变量,运行flutter doctor来显示剩下须要安装的依赖,国内用户最好配置一下两个环境变量:git
PUB_HOSTED_URL=https://pub.flutter-io.cn
复制代码
以下图所示: github
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
复制代码
如图所示: web
$ git clone -b beta https://github.com/flutter/flutter.git
复制代码
如图所示: 编程
打开一个新的命令提示符窗口,运行下面命令flutter doctor,看是否须要安装任何依赖项来完成安装,这个命令会检测环境和在终端生成报告,Dart SDK和Flutter捆绑在一块儿,不必单独去安装Dart。初次运行它会下载本身的依赖库而且自行编译,可能比较慢。后续运行flutter命令就会很快。这里注意,若是CMD窗口显示乱码问题,下面是解决方案:数组
HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe
若是该项下已存在CodePage项,则把值改成十进制”65001”,若是不存在,在该项下新建一个 DWORD(32位值),命名为“CodePage”,值设为“65001”flutter doctor --android-licenses
一直按y便可。最后从新输入flutter doctor
,结果以下图:
在Android Studio--File--Settings --plugins搜索Flutter便可,以下图: 服务器
由于如今是要建立应用程序,所以在Create New Flutter Project
下选择Flutter Application
: 微信
在下一个页面输入项目名字,配置Flutter SDK目录,项目存放的路径,项目的描述,公司的域名,项目的包名如图所示:
项目创建完成后,编辑器给咱们生成的目录以下:
整体来看和原生Android的工程结构不同了,由于代码都是在lib目录完成的,因此不能用Android多module多lib结构去建立module和lib,除非要用到原生交互的代码,能够在android目录里面去写,而后在lib目录里面去引用。相对Android开发者而言,多了ios目录,ios开发者而言,多了android目录,其余文件上面有具体详细说明。由于lib目录是开发者最主要关注的,打开lib目录,发现有个后缀是dart的文件,里面内容都是用dart语法来写的,还发现有void main() => runApp(MyApp());
main()函数对于学过java而言都很是清晰,这个应该是入口函数,另外发现StatelessWidget,StatefulWidget一些小控件,这里想应该是UI组件吧。这里先无论那么多,直接点击运行图标,运行效果图以下:
如今仍是按照新手走,先本身撸个“Hello world”出来吧。把main.dart中全部代码去掉,替换下面代码,屏幕中心显示Hello World
:
import 'package:flutter/material.dart';
//这个是Dart中单行函数或者方法的简写
void main() => runApp(MyApp());
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
// 这个是应用的根widget
@override
Widget build(BuildContext context) {
//注意:一个app只能有一个MaterialApp
return MaterialApp(
//标题栏的名字
title: 'Hello Flutter',
//这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
//包含主屏幕的widget树的body属性
home:new Scaffold(
appBar:new AppBar(
title:const Text("Weclome to Flutter"), ), body:const Center( child:const Text("Hello World"), ), ), );
}
}
复制代码
Android模拟器运行效果以下:
import 'dart:io';
import 'package:flutter/services.dart';
复制代码
//入口函数
void main() {
//MaterialApp组件渲染后
runApp(MyApp());
//判断若是是Android版本的话 设置Android状态栏透明沉浸式
if(Platform.isAndroid){
//写在组件渲染以后,是为了在渲染后进行设置赋值,覆盖状态栏,写在渲染以前对MaterialApp组件会覆盖这个值。
SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
}
复制代码
还有另一种方法,由于Flutter主入口只有一个MainActivity,也就是说全部的Flutter页面都会运行这个MainActivity,那咱们只须要在这个主入口判断一下版本号而后将状态栏颜色设置成透明,具体位置在android->app->src->main->xxx->MainActivity
:
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置状态栏透明
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
{//API>21,设置状态栏颜色透明
getWindow().setStatusBarColor(0);
}
GeneratedPluginRegistrant.registerWith(this);
}
}
复制代码
最终效果Android和iOS下运行以下:
appBar:new AppBar(
//ios和android标题栏统一在中间
title:new Center(child :const Text("Weclome to Flutter")), ), 复制代码
最终效果以下:
如今,经过在pubspec.yaml文件配置依赖项,去依赖一个提供英文单词的包,在pub.dartlang.org/flutter/这个网站能够找到不少开源软件包,能够搜索指定的软件包查看对应版本。
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
english_words: ^3.1.5
复制代码
flutter packages get
in flutter_demo...
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
// 这个是应用的根widget
@override
Widget build(BuildContext context) {
//随机生成函数
var wordPair = new WordPair.random();
return MaterialApp(
//标题栏的名字
title: 'Hello Flutter',
//这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
//包含主屏幕的widget树的body属性
home:new Scaffold(
appBar:new AppBar(
//ios和android标题栏统一在中间
title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( //使用随机生成的英文单词 为何不能const呢 由于 内容发生变化 const是用来修饰常量的 child:new Text(wordPair.asPascalCase), ), ), );
}
}
复制代码
注意的是Center widget 和 Text widget要用new
来建立,由于内容修改了,并非常量了,这时候按保存就能够看到新的单词出如今屏幕中间文本了。
上面MyApp是继承StatelessWidget,StatelessWidget是无状态的也是不可控的,意思是其属性是不能改变的,全部的值都是最终的。在平时开发中,不少控件都须要根据特定场景改变自身的状态,那么Flutter有没有提供有状态的widget呢?答案确定是有的,Statefulwidget是有状态的,在其生命周期保持的状态可能会变化,实现一个有状态的widget至少须要两个类:StatefulWidgets类和State类。
main.dart
文件最底下://建立有状态的widget
class RandomWordsWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return new RandomWordsState();
}
}
复制代码
//用来保存RandomWords widget的状态
class RandomWordsState extends State<RandomWordsWidget>{
@override
Widget build(BuildContext buildContext){
var wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
复制代码
//程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
// 这个是应用的根widget
@override
Widget build(BuildContext context) {
return MaterialApp(
//标题栏的名字
title: 'Hello Flutter',
//这个是Material library提供的一个widget,它提供了默认的导航栏、标题栏
//包含主屏幕的widget树的body属性
home:new Scaffold(
appBar:new AppBar(
//ios和android标题栏统一在中间
title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( child:new RandomWordsWidget() ), ), );
}
复制代码
这时候运行的效果仍是跟以前同样,不过实现方式不同。
下面建立一个滑动组件ListView,开发者接触最多应该是这个滑动组件,下面实现当用户滑动列表的时候,ListView会不断增加,不断显示新的单词。
final _normalWords = <WordPair>[];
复制代码
_buildNormalWords
方法,用于构建一个ListView,ListView提供了itemBuilder属性,这是一个工厂builder做为匿名函数进行回调,这个函数须要传入两个参数,一个是BuildContext上下文和行迭代器。对于ListView每一行都会执行这个函数调用,这里想一想好像是平时Android开发中ListView建立添加item的方法。//建立填充单词的ListView
Widget _buildNormalWorlds() {
//内容上下16dp
return new ListView.builder(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
//每一个单词都会条约一次itemBuilder,而后将单词添加到ListTile中
itemBuilder: (context, i) {
//首先建立10条单词
if (i >= _normalWords.length) {
//接着再生成10个单词,添加到列表上
_normalWords.addAll(generateWordPairs().take(10));
}
return _buildItem(_normalWords[i]);
});
}
复制代码
//设置每一个item项的内容和样式
Widget _buildItem(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
textAlign: TextAlign.center,
),
);
}
复制代码
build
方法,增长_buildNormalWords
方法的调用,不是直接用单生成库,代方法以下:Widget build(BuildContext buildContext) {
// var wordPair = new WordPair.random();删掉
// return new Text(wordPair.asPascalCase);删掉
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Weclome to Flutter")), ), body:_buildNormalWorlds(), );
}
复制代码
build
方法,由于标题的设置放在了RandomWordsState里了,因此不须要再额外添加,并将home变成RandomWords widget,代码一会儿简洁不少以下://程序继承StatelessWidget,该应用程序成为一个widget,在Flutter中,大多数东西都是widget
class MyApp extends StatelessWidget {
// 这个是应用的根widget
@override
Widget build(BuildContext context) {
return MaterialApp(
home:new RandomWordsWidget(),
);
}
}
复制代码
最终效果如图所示
_buildNormalWorlds
方法里判断是奇数项的话就构造出分割线添加到ListView,这里注意分割线也是一项,那么生成单词的时候要稍微处理下,具体代码以下://建立填充单词的ListView
Widget _buildNormalWorlds() {
//内容上下16dp
return new ListView.builder(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
//每一个单词都会条约一次itemBuilder,而后将单词添加到ListTile中
itemBuilder: (context, i) {
//添加分割线
//奇数行,会添加一个分割线的widget,分割上下单词
//偶数行,就正常构造添加ListTitle row
//构造一像素的分割线 也能够本身去定义宽高
//i.isOdd判断是否奇数,奇数行添加分割线
if(i.isOdd){
//注意:这里执行后会跳出循环
return new Divider();
}
//这里对2求商,就是计算出ListView中减去分割线后的实际单词数量
//如 i为 1,2,3,4,5,那么商结果是0,1,1,2,2
//1 除以 2商是0 2除以2商是1 以此类推
final int index = i ~/2;
//首先建立10条单词
if (index >= _normalWords.length) {
//接着再生成10个单词,添加到列表上
_normalWords.addAll(generateWordPairs().take(10));
}
return _buildItem(_normalWords[index]);
});
}
复制代码
实际效果以下:
_collected
的Set集合,这个集合用来存放收藏后的单词,用Set的缘由是Set自己的特性不容许元素有重复值:final Set<WordPair> _collected = new Set<WordPair>();
复制代码
_buildItem
方法中添加isCollected
来检查单词是否添加到收藏里final bool isCollected = _collected.contains(pair);
复制代码
_buildItem
之后置属性来添加一个❤️图标到ListTiles,matrial包下有默认的❤️图标,这里就设置下颜色就能够,代码以下:return new ListTile(
title: new Text(
pair.asPascalCase,
textAlign: TextAlign.center,
),
//trailing 是后置图标属性
trailing: new Icon(
//material 包下 icons.dart
isCollected ? Icons.favorite : Icons.favorite_border,
//图标颜色设置
color:isCollected ? Colors.red : null,
),
);
复制代码
运行结果后发现❤️型图标添加到每一行最右边处。
//trailing 是后置图标属性
trailing: new Icon(
//material 包下 icons.dart
isCollected ? Icons.favorite : Icons.favorite_border,
//图标颜色设置
color:isCollected ? Colors.red : null,
),
//item的点击事件属性
onTap:(){
//状态设置
setState(() {
//若是收藏了
if(isCollected){
//那就将set集合里移除
_collected.remove(pair);
} else {
//添加
_collected.add(pair);
}
});
}
复制代码
效果以下:
Widget build(BuildContext buildContext) {
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Weclome to Flutter")), //增长图标和点击事件动做 Icons.list是icon的类型 onPressed添加点击事件 点击会执行_collectWordsPage方法 actions:<Widget>[ new IconButton(icon: const Icon(Icons.list),onPressed: _collectWordsPage), ], ), body:_buildNormalWorlds(), );
}
复制代码
实际运行后,AppBar导航栏最右边的位置添加了一个图标,这时候点击没有任何反应,由于_collectWordsPage没有任何代码,下面添加跳转到新页面的实现。
//跳转新页面开始
void _collectWordsPage(){
Navigator.of(context).push(
);
}
复制代码
toList
来转换。MaterialPageRoute
是一种模态路由,能够经过平台自适应来切换屏幕,Android而言,页面推送过渡向上滑动页面,淡入淡出,弹出过渡则是向下滑动页面。IOS而言,页面从右侧滑入,反向弹出,当另外一个页面进入覆盖时,该页面向左移动。//跳转新页面开始
void _collectWordsPage() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
//传递收藏后的单词
final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase, //单词
),
);
},
);
},
),
);
}
复制代码
Navigator.pop
,点击返回按钮会返回到主界面,代码以下://跳转新页面开始
void _collectWordsPage() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
//传递收藏后的单词
final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase, //单词
),
);
},
);
//添加分割线开始
final List<Widget> divided = ListTile.divideTiles(
tiles: tiles,
context:context,
).toList();
return new Scaffold(
appBar: new AppBar(
title: new Center(child: const Text("Collect Words")), ), body: new ListView(children: divided), );
},
),
);
}
复制代码
最终运行效果发现标题并非居中对齐,由于默认的有边距,这时候须要自定义AppBar就能够,代码以下:
return new Scaffold(
appBar: new AppBar(
titleSpacing: 0.0,
//MaterialPageRoute这个自带了返回键 下面的属性设置取消返回键
automaticallyImplyLeading:false,
title: new Container(decoration: new BoxDecoration(color: new Color(0x00000000),
),
child:new Stack(
children: <Widget>[
new Container(
//左边位置
alignment: Alignment.centerLeft,
child:new IconButton(
//图标样式
icon:new Icon(Icons.arrow_back),
//点击事件
onPressed: (){
Navigator.pop(context);
}
),
),
new Center(child:new Text("Collect Words")),
],
),)
),
body: new ListView(children: divided),
);
复制代码
最终运行效果以下,左边是IOS,右边是Android:
入门Flutter的第一天,简单知道了一下几点:
Widget
,它中间层只有C/C++代码,Flutter
使用Dart语言实现系统的绝大部分功能(布局,动画,手势)。如有错误,欢迎指正~