flutter初体验之编写的第一个flutter app
随着flutter环境的搭建成功,我趁热打铁照着教程做起了第一个app的demo
这个教程分两步,
第一步
我还是跟他(https://flutter.cn/docs/get-started/codelab)一步步下来,感觉挺有收获的,编码习惯,编码语法之类的;项目也正常运行起来,没有问题:

第二步
-
像列表添加图标
-
添加交互
-
导航到新页面
-
使用themes修改UI
第一部分的时候,就犯难了
教程里是组件化的,这个没有问题,但是在使用widget组件的时候,出现问题了。
Widget _buildRow(WordPair pair) { final bool alreadySaved = _saved.contains(pair); return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), trailing: new Icon( // 新增代码开始 ... alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), // ... 新增代码结束 );}
在使用每个组件UI的时候 都要 new 一下,常规代码逻辑中是直接使用的如下
body: ListView.builder( padding: const EdgeInsets.all(16.0), itemBuilder: /*1*/ (context,i){ if(i.isOdd) return const Divider(); /*2*/ final index = i ~/2; /*3*/ if(index >= _suggestions.length){ _suggestions.addAll(generateWordPairs().take(10));/*4*/ } return ListTile( title: Text( _suggestions[index].asPascalCase, style: _biggerFont, ) ); } ), // /*1*/ 对于每个建议的单词对都会调用一次 itemBuilder,然后将单词对添加到 ListTile 行中。在偶数行,该函数会为单词对添加一个 ListTile row,在奇数行,该函数会添加一个分割线的 widget,来分隔相邻的词对。注意,在小屏幕上,分割线看起来可能比较吃力。 // /*2*/ 在 ListView 里的每一行之前,添加一个 1 像素高的分隔线 widget。 // /*3*/ 语法 i ~/ 2 表示 i 除以 2,但返回值是整型(向下取整),比如 i 为:1, 2, 3, 4, 5 时,结果为 0, 1, 1, 2, 2,这个可以计算出 ListView 中减去分隔线后的实际单词对数量。 // /*4*/ 如果是建议列表中最后一个单词对,接着再生成 10 个单词对,然后添加到建议列表。 // 对于每一个单词对,_buildSuggestions() 都会调用一次 _buildRow()。这个函数在 ListTile 中显示每个新词对,这使你在下一步中可以生成更漂亮的显示行,详见本 codelab 的第二部分。
导致跟着教程编写的时候一直报错,于是我去掉了 new 的关键字之后,重启了服务,列表中添加图标的功能实现了,如下图

但是这里没有交互
完整代码
import 'package:english_words/english_words.dart'; import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class RandomWords extends StatefulWidget{ const RandomWords({Key? key}): super(key:key); @override _RandomWordsState createState()=> _RandomWordsState(); } class _RandomWordsState extends State<RandomWords>{ final List<WordPair> _suggestions = <WordPair>[]; final Set<WordPair> _saved = new Set<WordPair>(); final TextStyle _biggerFont = const TextStyle(fontSize: 18.0); // 3 导航到新页面 void _pushSaved(){ Navigator.of(context).push( MaterialPageRoute<void>( builder: (BuildContext context){ final Iterable<ListTile> titls = _saved.map( (WordPair pair){ return ListTile( title: Text( pair.asPascalCase, style: _biggerFont, ) ); } ); final List<Widget> divided = ListTile.divideTiles( context: context, tiles: titls, ).toList(); return Scaffold( appBar: AppBar( title: const Text('saved name is huYe'), ), body: ListView(children: divided), ); } ) ); } @override Widget build(BuildContext context){ return Scaffold( appBar: AppBar( title: const Text('Startup Name Cenerator'), actions: <Widget>[ IconButton(onPressed: _pushSaved, icon: const Icon(Icons.list)) ], ), body: _buildSuggestions() ); } Widget _buildSuggestions() { return ListView.builder( padding: const EdgeInsets.all(16.0), itemBuilder: (BuildContext _context, int i) { if (i.isOdd) { return const Divider(); } final int index = i ~/ 2; if (index >= _suggestions.length) { _suggestions.addAll(generateWordPairs().take(10)); } return _buildRow(_suggestions[index]); }); } // 2.1 添加图标 Widget _buildRow(WordPair wp){ final bool alreadySaved = _saved.contains(wp); return ListTile( title: Text( wp.asPascalCase, style: _biggerFont, ), trailing: Icon( alreadySaved? Icons.favorite : Icons.favorite_border, color: alreadySaved? Colors.red : null, ), // 2.2 添加交互 onTap: (){ setState(() {//在 Flutter 的响应式风格的框架中,调用 setState() 会为 State 对象触发 build() 方法,从而导致对 UI 的更新 if(alreadySaved){ _saved.remove(wp); }else{ _saved.add(wp); } }); }, ); } } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { // 这里自己用了const 代码报错,应该用final return MaterialApp( title: 'Startup Name Generators', home: const RandomWords(), theme: ThemeData( primaryColor: Colors.red // 不论如何设置都无效(后续补充这的原因) ), );

浙公网安备 33010602011771号