目录
前言
Flutter个人感觉是移动端开发不错的姿势,相比于其他weex开发也有一定优势。如果你想先初步了解Flutter的话,可以看下这个视频介绍。20多分钟,基本讲述了Flutter的优点和设计思想。对于新入手的人而言,值得花点时间研究下。
另外本篇博客内容取自于Flutter官网教程,用于记录所获,如果你希望更加获得更加官方的咨询,可参见官网。
注意
- 上面视频中有个非常炫酷的操作,就是通过改变framework(就是Flutter提供给你的代码),直接修改一个list的表现形式。喂这是正常操作吧,这里的Flutter对于整个ios或者Android来说就是一个三方包,只是经过编译之后变成arm指令。那么你修改三方包里面的源代码,导致表现形式改变不是很正常嘛?忽悠人,差评。
- 在Flutter中要建立组件化(widget)的概念,什么都是组件,连整个app都是组件,连padding都是组件,连居中都是组件!
- UI从命令式到声明式的转变。怎么理解这句话,这对于Flutter的理解非常重要。
从上面这张图来说,假设由于某个时间触发,我们需要将当前的view从 左图变到右图。对于从win32就开始的传统的命令式UI来说,首先我们需要获取到ViewB b ,ViewC c1 ViewC c2的实例,然后修改这些实例的状态,比如用android的代码来写可能就差不多是这样的。
但是在声明式的UI系统中,view是不可变的,所以所有的视图都是轻量级的“蓝图”,当需要改变的时候,就会重新构建这个view(Flutter中一遍就是StatefulWidget组件通过State中调用setState()方法):b.setColor(red) b.clearChildren() ViewC c3 = new ViewC(...) b.add(c3)
至于那种模式好,效率高当然是各家之言,等到我足够体验之后才能做出自己的判断。return ViewB( color: red, child: ViewC(...), )
Hello world!
//使用了material的包,Flutter还提供一个cupertion包,用于表示谷歌和苹果两种界面风格
//当然我们完全可以自定义自己的风格
import 'package:flutter/material.dart';
//引入了一个三方库 english_words ,需要在pubspec.yaml中的dependencies: 下面添加 english_words: ^3.1.0
import 'package:english_words/english_words.dart';
//main是dart的主方法,主方法很简单,调用系统方法runApp,运行一个App
void main() => runApp(MyApp());
//MyApp 就是我们的app了,它继承与StatelessWidget组件
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) { //build是组件类的基本方法,用于构建这个组件的显示样式
final wordPair = WordPair.random();
return MaterialApp( //一个MaterialApp widget
title: 'Welcome to Flutter',
home: Scaffold( //一个Scaffold widget相当于主界面,包含一个组件树
appBar: AppBar( //标题栏组件
title: Text('Welcome to Flutter IOS'),
),
body: Center( //center组件,居中显示
child: Text(wordPair.asPascalCase),
),
),
);
}
}
这段代码以及注释希望能够让你对Flutter有一个初步映像,至于其中包含的上面appBar啊Center啊这些组件,真的是在开发过程中逐步积累的过程,并不是入门时的重点。
Widget
组件widget分为两种,一种不不可变的StatelessWidget他内部的变量都是final类型的,所以是属性不可变的组件。另一种是StatefulWidget组件。
实现StatefulWidget至少需要两个类:一个StatefulWidget类 2)State<?>类的实例。 StatefulWidget类本身是不可变的,但State类在窗口小部件的生命周期内持久存在。
扩展一下上面代码
//使用了material的包,Flutter还提供一个cupertion包,用于表示谷歌和苹果两种界面风格
//当然我们完全可以自定义自己的风格
import 'package:flutter/material.dart';
//引入了一个三方库 english_words ,需要在pubspec.yaml中的dependencies: 下面添加 english_words: ^3.1.0
import 'package:english_words/english_words.dart';
//main是dart的主方法,主方法很简单,调用系统方法runApp,运行一个App
void main() => runApp(MyApp());
//MyApp 就是我们的app了,它继承与StatelessWidget组件
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) { //build是组件类的基本方法,用于构建这个组件的显示样式
return MaterialApp( //一个MaterialApp widget
title: 'Welcome to Flutter',
home: Scaffold( //一个Scaffold widget相当于主界面,包含一个组件树
appBar: AppBar( //标题栏组件
title: Text('Welcome to Flutter IOS'),
),
body: Center( //center组件,居中显示
child: RandomWords() //使用StatefulWidget组件
),
),
);
}
}
/*
* 为了实现StatefulWidget,必须要有一个State类,用于帮助StatefulWidget build它的显示样式。
* 实际上,大多数的业务逻辑都是在State中进行处理的。
*/
class RandomWordsState extends State<RandomWords>{
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
}
/*
* 定义一个用于显示随机单词的组件,这个组件是一个StatefulWidget组件,所以我们必须要提供一个State<RandomWords>的类
* StatefulWidget 内部必须重写createState方法。他的widget的build方法交给这个State去写。
*/
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return RandomWordsState();
}
}
扩展成无限滑动的list
首先下面代码的主要目的并不是让我们掌握Flutter中list的具体用法,个人觉得应该亲自去写一下代码,知道这里在干什么,去了解Flutter的代码构成方式和体会“一切都是组件”的含义。
//使用了material的包,Flutter还提供一个cupertion包,用于表示谷歌和苹果两种界面风格
//当然我们完全可以自定义自己的风格
import 'package:flutter/material.dart';
//引入了一个三方库 english_words ,需要在pubspec.yaml中的dependencies: 下面添加 english_words: ^3.1.0
import 'package:english_words/english_words.dart';
//main是dart的主方法,主方法很简单,调用系统方法runApp,运行一个App
void main() => runApp(MyApp());
//MyApp 就是我们的app了,它继承与StatelessWidget组件
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) { //build是组件类的基本方法,用于构建这个组件的显示样式
return MaterialApp( //一个MaterialApp widget
title: 'Welcome to Flutter',
home: RandomWords(), //主界面是一个RandomWords类型的组件
);
}
}
/*
* 为了实现StatefulWidget,必须要有一个State类,用于帮助StatefulWidget build它的显示样式。
* 实际上,大多数的业务逻辑都是在State中进行处理的。
*/
class RandomWordsState extends State<RandomWords>{
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
/*
* 用于创建一个ListView组件
*/
Widget _buildSuggestions(){
//listview的builder工厂方法
return ListView.builder(padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i){ /* dart写法,类似于匿名类和lambda */
//如果是基数,返回分割线
if(i.isOdd) return Divider();
final index = i ~/ 2; //i除以2 并返回整数结果
if(index >= _suggestions.length){
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
/*
* 创建listview中的每一个item,每个item都是一个组件
*/
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
/*
* 创建RandomWords组件的界面展示。
*/
@override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text('Startup Name Generator'),
),
body: _buildSuggestions(),
);
}
}
/*
* 定义一个用于显示随机单词的组件,这个组件是一个StatefulWidget组件,所以我们必须要提供一个State<RandomWords>的类
* StatefulWidget 内部必须重写createState方法。他的widget的build方法交给这个State去写。
*/
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return RandomWordsState();
}
}
添加页面跳转和点击
继续改造上面的代码,添加了随机单词标星这样的操作。并且能够进行页面跳转。
//使用了material的包,Flutter还提供一个cupertion包,用于表示谷歌和苹果两种界面风格
//当然我们完全可以自定义自己的风格
import 'package:flutter/material.dart';
//引入了一个三方库 english_words ,需要在pubspec.yaml中的dependencies: 下面添加 english_words: ^3.1.0
import 'package:english_words/english_words.dart';
//main是dart的主方法,主方法很简单,调用系统方法runApp,运行一个App
void main() => runApp(MyApp());
//MyApp 就是我们的app了,它继承与StatelessWidget组件
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) { //build是组件类的基本方法,用于构建这个组件的显示样式
return MaterialApp( //一个MaterialApp widget
title: 'Welcome to Flutter',
home: RandomWords(), //主界面是一个RandomWords类型的组件
theme: new ThemeData(primaryColor: Colors.white), //修改app主题
);
}
}
/*
* 为了实现StatefulWidget,必须要有一个State类,用于帮助StatefulWidget build它的显示样式。
* 实际上,大多数的业务逻辑都是在State中进行处理的。
*/
class RandomWordsState extends State<RandomWords>{
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final Set<WordPair> _saved = new Set<WordPair>(); //用于存储已经点赞的名字
/*
* 用于创建一个ListView组件
*/
Widget _buildSuggestions(){
//listview的builder工厂方法
return ListView.builder(padding: const EdgeInsets.all(16.0),
itemBuilder: (context,i){ /* dart写法,类似于匿名类和lambda */
//如果是基数,返回分割线
if(i.isOdd) return Divider();
final index = i ~/ 2; //i除以2 并返回整数结果
if(index >= _suggestions.length){
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
},
);
}
/*
* 创建listview中的每一个item,每个item都是一个组件
*/
Widget _buildRow(WordPair pair) {
final bool alreadySaved = _saved.contains(pair); //记录是否已经标记过
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(alreadySaved? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red:null,),//创建一个星形icon
//icon点击事件
onTap: (){setState(() { //使用setState来更新list中item的状态,setState会先运行下面代码,然后再通知list更改item
if(alreadySaved){
_saved.remove(pair);
}else{
_saved.add(pair);
}
});},
);
}
/*
* 创建RandomWords组件的界面展示。
*/
@override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text('Startup Name Generatorrrrrr'),
//为app bar增加左上按钮
actions: <Widget>[IconButton(icon: const Icon(Icons.list),onPressed: _pushSaved)],
),
body: _buildSuggestions(),
);
}
/*
* appbar 左上按钮点击事件。使用Navigator 跳转到新页面
* Navigator 是Flutter中的路由管理器,通过push进行跳转,通过remove进行返回
*/
void _pushSaved(){
//在路由管理器 Navigator中push一个新的路由
//这个路由是一个页面路由,通过builder构建这个页面
Navigator.of(context).push(MaterialPageRoute<void>(builder: (context){
//dart语法,每一个被标星的单词都会生成一个ListTile组件
final Iterable<ListTile> tiles = _saved.map(
(pair){
return new ListTile(
title: new Text(pair.asPascalCase,style:_biggerFont),
);
}
);
//在tiles组件之间插入分割线
final List<Widget> divided = ListTile.divideTiles(context: context,tiles: tiles).toList();
return new Scaffold(
appBar: new AppBar(title: const Text('Saved Suggestions'),),
body: new ListView(children: divided,),//生成listview
);
}));
}
}
/*
* 定义一个用于显示随机单词的组件,这个组件是一个StatefulWidget组件,所以我们必须要提供一个State<RandomWords>的类
* StatefulWidget 内部必须重写createState方法。他的widget的build方法交给这个State去写。
*/
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return RandomWordsState();
}
}
结语
基本上所有的分析都写在注释中,所以文章本身并没有多少文字,都是贴代码。如果能够跟着上面的步骤,将代码好好读一遍,那么应该就基本上算是初步了解了Flutter的代码构成。当然这只是万里长征的第一步,更多的资源可以参考这里。
另外上面的代码并没有进行非常好的代码结构处理,而是一直在dart提供的函数式编程能力。这样就导致代码层次嵌套很多(如果老老实实用局部变量,就能够极大的减少嵌套),对后续阅读产生困扰,所以语法糖这种东西,并不是在任何时候都讨喜的。