Flutter第一个应用
这里是根据官方给的案例重新稍作整理,对基础工程以及界面交互等做一个初步的了解,具体知识点会在后面的文章做详细的介绍。通过本文可以了解到以下几点知识:
工程主要文件
首先我们创建一个Flutter项目,项目名称单词首字母小写,下划线连接,例如first_project,而不是firstProject或者FirstProject。创建项目完成后,如下图。今天我们主要会用到两个文件lib/main.dart、pubspec.yaml。main.dart是main方法所在的文件,项目的入口,pubspec.yaml里面是对第三方Packages的引用依赖。
项目创建后,main.dart文件里面会默认生成一些构建简单页面的代码以及一些注释。连接上设备,run一下,可以看到效果,如下图。
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "demo app",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Welcome to flutter"),
),
body: new Center(
child: new Text("Hello World"),
),
),
);
}
}
界面运行如下:引用外部的Package
这里以一个开源的软件包english_words来做个示例,这个包里包含了数千个常用的英文单词和一些功能。可以在pub.dartlang.org上查到很多有用的开源工具包。
pubspec.yaml文件管理着Flutter应用程序的静态资源。在文件中添加english_words依赖如下:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.0
english_words: ^3.1.0
点击右上角的Packages get,将包拉到项目中。控制台输出如下表示完成。
--no-color packages get
Running "flutter packages get" in first_project...
Process finished with exit code 0
将包导入到lib/main.dart中。import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
下面我们在代码中使用这个包里面的WordPair类,生成随机的单词,代替Hello World,代码如下。红线是新加的代码,然后点一下工具栏的Flutter Hot Reload按钮,就可以直接热重载界面。生成随机单词代替了Hello World。使用有状态的Widget
通过上面的代码可以看到,app继承了一个使它自己变成一个widget的StatelessWidget类。在Flutter里面,大多时候可以把一切都看作widget。widget主要方法是提供了一个build()方法,描述如何根据其他更低级别的widget来对这个widget进行展示。上面那段示例包含了Text child widget、Center widget,Center widget可以将它所有的子树都对齐到屏幕中心。
Stateless widget是不可以改变的,这就意味着它的属性也不能改变,所有的值都是final修饰的。Stateful widget保持的状态在生命周期内可能会发生变化,要实现一个有状态的widget至少需要两个条件:1)一个StatefulWidget类;2)这个类创建一个State类的实例。StatefulWidget类本身是不可变的,但是State类可以存在整个widget生命周期中。
此处的示例我们会在main.dart里创建一个有状态的widget类RandomWords继承StatefulWidget,再创建一个继承了State类的RandomWordsState类,然后在RandomWords类里实例化后者,RandomWordsState会保存RandomWords的状态,实现一个有状态的widget。代码如下。
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomWordsState();
}
}
class RandomWordsState extends State<RandomWords>{
@override
Widget build(BuildContext context) {
// TODO: implement build
}
}
下面将这个widget添加到我们的页面上去,在RandomWordsState的build里面描述要展示的内容。然后把这个widget展示到body里面去,展示的效果不变,代码如下:class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "demo app",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Welcome to flutter"),
),
body: new Center(
child: new RandomWords()
),
),
);
}
}
class RandomWords extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomWordsState();
}
}
class RandomWordsState extends State<RandomWords>{
final wordPair = new WordPair.random();
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Text(wordPair.asPascalCase);
}
}
大部分操作都是在RandomWordsState里面完成的,在这里RandomWords不需要做太多事。
ListView的使用
列表在页面布局里面也是很常见的,这里进一步扩展下RandomWordsState类,生成并展示词组列表。
首先来定义一个保存推荐词组的数组,还有个字体样式。
final _suggestions = <WordPair>[];
final _fontSize = new TextStyle(fontSize: 16.0);
创建一个_buildRow函数,传入参数WordPair,渲染ListView的每一行展示的widget树。Widget _buildRow(WordPair pair){
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _fontSize,
),
);
}
创建一个_buildSuggestions()函数,来构建ListView。ListView提供了一个itemBuilder属性,这是一个工厂build,并作为匿名函数回调。它有两个参数,context上下文和i行迭代器,对应数组里的每一个推荐词都会执行一次函数调用,迭代器从0开始,每调一次就会增加1。在本次示例里我会先添加20个推荐词到数组里面,然后渲染到ListView里面,_buildSuggestions代码如下。Widget _buildSuggestions(){
_suggestions.addAll(generateWordPairs().take(20));
return new ListView.builder(
itemBuilder: (context,i){
return _buildRow(_suggestions[i]);
}
);
}
这时候运行一下,发现列表出来了。但是上拉滑动的时候发现问题了,超出20个item之后还可以向下滚动,白屏,如图。出现这种情况是因为ListView的builder构造器运行按需建立延时加载的view。只需要判断下迭代器,超出长度后不渲染view就可以了,代码如下。现在运行就正常了。
Widget _buildSuggestions(){
_suggestions.addAll(generateWordPairs().take(20));
return new ListView.builder(
itemBuilder: (context,i){
if(i < _suggestions.length){
return _buildRow(_suggestions[i]);
}
}
);
}
现在我们想给列表每一列加上一条分割线,在这里我们也把分割线当做一个row添加到ListView里面去,一行数据隔一行分割线,具体原来代码里有注释。Widget _buildSuggestions(){
_suggestions.addAll(generateWordPairs().take(20));
return new ListView.builder(
itemBuilder: (context,i){
if(i.isOdd){return new Divider();}//如果i是奇数,就添加分割线,如果是偶数,才添加带数据的row
final index = i ~/ 2;//效果和整除差不多,i为0、1、2、3、4、5...,算到的index为0、0、1、1、2、2...
//index的计算,主要是因为插入了分割线以后,迭代器迭代的次数是原来的双倍,一半是渲染分割线,一半是渲染带数据的row
if(index < _suggestions.length){
return _buildRow(_suggestions[index]);
}
}
);
}
交互操作
这里我们为每一行添加一个心型图,类似点赞,可以点赞和取消点赞的效果。首先我建一个Set集合来保存我们点过赞的单词,Set集合不允许重复数据,比List合适。
final _saved = new Set<WordPair>();
在每一个row添加心型图,并声明一个aleardySaved判断是否被点赞。Widget _buildRow(WordPair pair){
final aleardySaved = _saved.contains(pair);//判断是否包含当前单词,判断是否被赞
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _fontSize,
),
trailing: new Icon(
aleardySaved ? Icons.favorite : Icons.favorite_border,
color: aleardySaved ? Colors.red:null,
),
);
}
此时点击还没有任何效果,还需要给每个row添加点击事件。Widget _buildRow(WordPair pair){
final aleardySaved = _saved.contains(pair);//判断是否包含当前单词,判断是否被赞
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _fontSize,
),
trailing: new Icon(
aleardySaved ? Icons.favorite : Icons.favorite_border,
color: aleardySaved ? Colors.red:null,
),
onTap: () => _favourite(pair),
);
}
void _favourite(WordPair pair){
setState((){
if(_saved.contains(pair)){
_saved.remove(pair);
}else{
_saved.add(pair);
}
});
}
现在就达到我们要的效果,点击变红色并保存,再次点击变成默认的,并从set集合移除。跳转到新页面
在页面导航栏加一个按钮,跳转到新的页面,Navigator管理着包含了应用程序所有路由的堆栈,将一个路由push到堆栈,将显示更新到新的页面路由,将一个路由pull出堆栈,将返回显示前一个页面路由。
return new Scaffold(
appBar: new AppBar(
title: new Text("Welcome to flutter"),
actions: <Widget>[new IconButton(icon: new Icon(Icons.list), onPressed: _pushNavi)],
),
body: _buildSuggestions()
);
void _pushNavi(){
Navigator.of(context).push(
new MaterialPageRoute(
builder:(context){
return new Scaffold(
appBar: new AppBar(
title: new Text("Welcome to new page"),
),
body: new Center(
child: new Text("new page"),
),
);
})
);
}
效果如下:上一篇: 第一个Flutter APP
推荐阅读