Flutter(二) 创建第一个Flutter App
这一章主要是创建一个Flutter App。如果你熟悉面向对象编程,有基本的编程概念(变量,循环,条件判断等),那么你不必要具备原有的Dart和移动开发经验,,就可以轻松地理解完成这章内容。
你将为一家初创公司实现一个简单的移动app,主要功能是为这家公司推荐名字。用户可以选择或者取消名字,保存最好的名字。程序会一次性产生10个名字。在用户滚动时,新名字同时会产生出来。用户可以点击导航栏(appbar)的列表图标进入到一个新的列表页查看喜欢的名字。 最终的结果最后运行结果中可以看到。
创建第一个简单的,IDE提供模板的Flutter App,可以按照引导创建Flutter工程。我将工程名命名为flutter_app,这个按照个人习惯吧,只要名称合法就行。 在这节程序实验中,最多编辑的会是lib/main.dart,其中就是Dart代码。
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),),body: new Center(
child: new Text('Hello World'),);
}
}
结果
这一步中,你将使用开源包english_words。这个包中包含了几千个最常用的英文单词和一些实用方法。 你可以在pub.dartlang.org找到english_words包,同时还有其他的开源包。
...
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.0
english_words: ^3.1.0
...
原有模板文件中代码太多(包含注释),不全部展示出来。这里在depenencies下添加english_words包依赖。
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
在你输入后,AS会因为你写的导入语句给出建议。表现在你输入的导入语句会变成灰色,这就是提示你导入的库还没有使用过。
针对原有代码做出修改 import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new MaterialApp(
title: 'Welcome to Flutter',home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),body: new Center(
child: new Text(wordPair.asPascalCase),);
}
}
无状态组件(Stateless widget)是不可变的,意味着他们的属性无法改变——所有值是final的。 有状态组件(Stateful widget)维持一个状态值,此状态值会根据组件生命周期而有所改变。实现一个有Stateful widget需要至少两个类: StatefulWidget组件本身是不可变的,但是State类在整个组件声明周期过程中是始终存在的。 在这步中,你将会添加一个stateful widget,RandomWords以及创建对应的状态State类,RandomWordsState。State类实际上为组件保留着喜欢的单词对。
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
}
在RandomWordsState的build()方法中添加代码,整个文件看起来像这样 import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),body: new Center(
child: new RandomWords(),);
}
}
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
在这步中,你将扩展RandomWordsState类来展示一个列表。列表随着用户的滚动无限增粘。ListView的builder工厂方法可以按需要进行懒加载。
同时添加比变量 biggerFont 改变字体大小。 class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
Listview提供了builder属性itemBuilder 用以产生item及匿名函数的回调。BuildContext和行的迭代器索引 i ,这两个参数被传到ListView的buil()方法。迭代器索引从0开始增长,每次方法调用产生一个单词对的时候就会增长。这就使得列表在用户滚动时无限增长。 class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>();
.....
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),// The itemBuilder callback is called once per suggested word pairing,
// and places each suggestion into a ListTile row.
// For even rows,the function adds a ListTile row for the word pairing.
// For odd rows,the function adds a Divider widget to visually
// separate the entries. Note that the divider may be difficult
// to see on smaller devices.
itemBuilder: (context,i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return new Divider();
// The syntax "i ~/ 2" divides i by 2 and returns an integer result.
// For example: 1,2,3,4,5 becomes 0,1,2.
// This calculates the actual number of word pairings in the ListView,
// minus the divider widgets.
final index = i ~/ 2;
// If you've reached the end of the available word pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
}
在RandomWordsState中添加 _buildRow()方法 class RandomWordsState extends State<RandomWords> {
...
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,style: _biggerFont,);
}
}
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',home: new RandomWords(),);
}
}
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return new Scaffold (
appBar: new AppBar(
title: new Text('Startup Name Generator'),body: _buildSuggestions(),);
}
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// minus the divider widgets.
final index = i ~/ 2;
// If you've reached the end of the available word pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,);
}
}
这步中,你讲为每行item添加一个可点击的心形图标,在用户点击item时,对应的单词对(word pair)会被添加到收藏或者被移除。
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>();
...
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
...
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,trailing: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,color: alreadySaved ? Colors.red : null,);
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},);
}
重运行App。你应该可以通过点击来添加或者取消收藏。注意的一点是,在点击的时候可以看到一个放射性的点击效果,这是Material风格所致。如果有Android开发经验的程序员就会知道。
在这步中,你将添加一个新屏幕(在Flutter叫做route)来展示你的收藏。你将学习如何在home route和新route之间进行交互。 在Flutter中,导航器(Navigator)管理着所有app route的一个栈。向栈内push一个route就表示这将展示新的一屏。pop出栈表示向前显示一屏。
在方法中添加icon和对应的action: ....
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>();
@override
Widget build(BuildContext context) {
return new Scaffold (
appBar: new AppBar(
title: new Text('Startup Name Generator'),actions: <Widget>[
new IconButton(icon: new Icon(Icons.list),onPressed: _pushSaved),],);
}
....
}
class RandomWordsState extends State<RandomWords> {
...
void _pushSaved() {
}
}
...
此处方法中并未添加任何代码。
添加调用Navigator.push,将route推送的Navigator栈中。 ...
class RandomWordsState extends State<RandomWords> {
...
void _pushSaved() {
Navigator.of(context).push(
);
}
}
...
void _pushSaved() {
Navigator.of(context).push(new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,);
},);
final divided = ListTile
.divideTiles(
context: context,tiles: tiles,)
.toList();
},);
}
这样整体的代码如下 void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,);
},);
final divided = ListTile
.divideTiles(
context: context,)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('Saved Suggestions'),body: new ListView(children: divided),);
},);
}
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',);
}
}
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
final _saved = new Set<WordPair>();
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Startup Name Generator'),);
}
void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute(
builder: (context) {
final tiles = _saved.map(
(pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,);
},);
final divided = ListTile
.divideTiles(
context: context,)
.toList();
return new Scaffold(
appBar: new AppBar(
title: new Text('Saved Suggestions'),body: new ListView(children: divided),);
},);
}
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// minus the divider widgets.
final index = i ~/ 2;
// If you've reached the end of the available word pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,trailing: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,color: alreadySaved ? Colors.red : null,onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},);
}
}
这是最后一步,你将使用theme。主题(theme)主要控制app看起来外表如何。可以使用默认的主题,从文章开始到目前使用的一直是默认的主题。主题不依赖与物理设备或者模拟器。你也可以自定义自己的主题来突显你自己的品牌。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',theme: new ThemeData(
primaryColor: Colors.white,);
}
}
你可以通过修改引用来改变主题样式,自己试试吧。 好了,这章的内容就是这些了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |