├── .idea
├── flutter-cookbook.iml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── docs
├── animation
│ └── Fade_a_Widget_in_and_out.md
├── design_basics
│ ├── Add_a_Drawer_to_a_screen.md
│ ├── Displaying_SnackBars.md
│ ├── Exporting_fonts_from_a_package.md
│ ├── Updating_the_UI_based_on_orientation.md
│ ├── Using_Themes_to_share_colors_and_font_styles.md
│ ├── Using_custom_fonts.md
│ └── Working_with_Tabs.md
├── forms
│ ├── Building_a_form_with_validation.md
│ ├── Create_and_styled_a_text_field.md
│ ├── Focus_on_a_text_field.md
│ ├── Retrieve_the_value_of_a_text_field.md
│ └── handling_changes_to_a_text_field.md
├── handling_gestures
│ ├── Adding_Material_Touch_ripples.md
│ ├── Handling_Taps.md
│ └── Implement_Swipe_to_Dismiss.md
├── images
│ ├── Display_images_from_the_internet.md
│ ├── Fade_in_images_with_a_placeholder.md
│ └── Working_with_cached_images.md
├── lists
│ ├── Create_a_basic_list.md
│ ├── Creating_a_grid_List.md
│ ├── Creating_lists_with_different_types_of_items.md
│ ├── Make_a_horizontal_list.md
│ └── Working_with_long_lists.md
├── navigation
│ ├── Animating_a_Widget_across_screens.md
│ ├── Navigate_to_a_new_screen_and_back.md
│ ├── Navigate_with_named_routes.md
│ ├── Return_data_from_a_screen.md
│ └── Send_data_to_a_new_screen.md
├── networking
│ ├── Fetch_data_from_the_internet.md
│ ├── Making_authenticated_requests.md
│ ├── Parsing_JSON_in_the_background.md
│ └── Working_with_WebSockets.md
└── persistence
│ ├── Reading_and_Writing_Files.md
│ └── Storing_key-value_data_on_disk.md
└── relative
└── Flutter for React Native Developers.md
/.idea/flutter-cookbook.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
41 |
42 |
43 |
77 |
78 |
79 |
80 |
81 | true
82 | DEFINITION_ORDER
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 | 1522220234132
232 |
233 |
234 | 1522220234132
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter-Cookbook
2 | > [原文链接](https://flutter.io/cookbook/)
3 |
4 | 这本书包含了一些技巧,演示了如何在编写`flutter`应用程序的同时,解决一些常见的问题。每一篇都是独立的,可以用作参考,帮助你您构建应用程序。
5 |
6 | ## Design 设计
7 |
8 | - [使用Themes共享颜色和字体样式](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Using_Themes_to_share_colors_and_font_styles.md)
9 | - [显示SnackBar](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Displaying_SnackBars.md)
10 | - [使用Tabs](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Working_with_Tabs.md)
11 | - [使用自定义字体](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Using_custom_fonts.md)
12 | - [从包导出字体](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Exporting_fonts_from_a_package.md)
13 | - [添加侧边导航](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Add_a_Drawer_to_a_screen.md)
14 | - [根据横竖屏方向更新UI](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/design_basics/Updating_the_UI_based_on_orientation.md)
15 |
16 | ## Images 图像
17 |
18 | - [展示网络图片](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/images/Display_images_from_the_internet.md)
19 | - [淡入带有占位符的图像](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/images/Fade_in_images_with_a_placeholder.md)
20 | - [处理缓存的图片](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/images/Working_with_cached_images.md)
21 |
22 | ## Lists 列表
23 |
24 | - [创建基础列表](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/lists/Create_a_basic_list.md)
25 | - [创建水平列表](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/lists/Make_a_horizontal_list.md)
26 | - [处理长列表](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/lists/Working_with_long_lists.md)
27 | - [创建具有不同类型项的列表](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/lists/Creating_lists_with_different_types_of_items.md)
28 | - [创建网格列表](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/lists/Creating_a_grid_List.md)
29 |
30 | ## Gestures 手势
31 |
32 | - [处理点击事件](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/handling_gestures/Handling_Taps.md)
33 | - [添加Material波纹](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/handling_gestures/Adding_Material_Touch_ripples.md)
34 | - [滑动移除](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/handling_gestures/Implement_Swipe_to_Dismiss.md)
35 |
36 | ## Navigation 导航
37 |
38 | - [导航到新页面并返回](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Navigate_to_a_new_screen_and_back.md)
39 | - [发送数据到新页面](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Send_data_to_a_new_screen.md)
40 | - [从页面返回数据](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Return_data_from_a_screen.md)
41 | - [一个屏幕动画到另一个屏幕](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Animating_a_Widget_across_screens.md)
42 | - [导航到命名路由](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Navigate_with_named_routes.md)
43 |
44 | ## Animation 动画
45 |
46 | - [淡入淡出一个部件](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/animation/Fade_a_Widget_in_and_out.md)
47 |
48 | ## Networking 网络
49 |
50 | - [获取数据](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Fetch_data_from_the_internet.md)
51 | - [授权认证](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Making_authenticated_requests.md)
52 | - [WebSocket](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Working_with_WebSockets.md)
53 | - [后台解析JSON](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Parsing_JSON_in_the_background.md)
54 |
55 | ## Form 表单
56 |
57 | - [使用验证构建表单](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/forms/Building_a_form_with_validation.md)
58 | - [创建文本字段并添加样式](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/forms/Create_and_styled_a_text_field.md)
59 | - [使TextField获得焦点](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/forms/Focus_on_a_text_field.md)
60 | - [处理文本字段的更改](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/forms/handling_changes_to_a_text_field.md)
61 | - [获取文本字段的值](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/forms/Retrieve_the_value_of_a_text_field.md)
62 |
63 | ## Persistence 进阶
64 |
65 | - [内存中存储键值对](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/persistence/Storing_key-value_data_on_disk.md)
66 | - [读写文件](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/persistence/Reading_and_Writing_Files.md)
67 |
68 | ## 相关链接
69 |
70 | [Tour the framework](https://flutter.io/widgets-intro/)
71 |
72 | [Widgets Catalog](https://flutter.io/widgets/)
73 |
74 | [Sample Catalog](https://flutter.io/widgets/)
75 |
76 | [Building beautiful UIs-Codelab](https://codelabs.developers.google.com/codelabs/flutter/index.html#0)
77 |
78 | [Build layouts - Tutorial](https://flutter.io/tutorials/layout/)
79 |
80 | [Gestures](https://flutter.io/gestures/)
81 |
82 | [Animations](https://flutter.io/animations/)
83 |
84 | [Box constraints](https://flutter.io/layout/)
85 |
86 | [Assets and images](https://flutter.io/assets-and-images/)
87 |
88 | [Text input](https://flutter.io/text-input/)
89 |
90 | [Internationalization](https://flutter.io/tutorials/internationalization)
--------------------------------------------------------------------------------
/docs/animation/Fade_a_Widget_in_and_out.md:
--------------------------------------------------------------------------------
1 | # 淡入淡出一个部件
2 |
3 | 作为UI开发人员,我们通常需要在屏幕上显示和隐藏元素。然而,在屏幕上快速显示和隐藏元素会让用户感到不舒服。相反,我们可以淡出元素以及改变元素透明度,以创造一个平滑的体验。
4 |
5 | 在Flutter中,我们可以使用[`AnimatedOpacity`](https://docs.flutter.io/flutter/widgets/AnimatedOpacity-class.html)Widget来完成这个任务
6 |
7 | ## 指南
8 |
9 | 1、显示一个淡入淡出的盒子
10 |
11 | 2、定义一个`StatefulWidget`
12 |
13 | 3、显示一个按钮,以切换可见性
14 |
15 | 4、添加淡入淡出动画
16 |
17 | ## 1、显示一个淡入淡出的盒子
18 |
19 | 首先,我们需要一些东西来淡入淡出!在本例中,我们将在屏幕上绘制一个绿色背景的盒子。
20 |
21 | ```dart
22 | new Container(
23 | color: Colors.green,
24 | width: 200.0,
25 | height: 200.0
26 | )
27 | ```
28 |
29 | ## 2、定义一个`StatefulWidget`
30 |
31 | 现在我们有了一个绿色的盒子来实现动画,我们需要一种方法来知道这个盒子应该是可见的还是不可见的。要实现这一点,我们可以使用[`StatefulWidget`](https://docs.flutter.io/flutter/widgets/StatefulWidget-class.html)。
32 |
33 | `StatefulWidget`是一个创建`State`对象的类。`State`对象保存了一些有关我们应用程序的数据,并提供了更新改数据的方法。当我们更新数据时,我们也可以要求Flutter用这些更改重新构建我们的UI。
34 |
35 | 在本例中,我们将有一个数据:一个布尔值,用来表示按钮是可见的还是不可见的。
36 |
37 | 构建一个`StatefulWidget`类,我们需要创建两个类:一个`StatefulWidget`和相应的`State`类。小贴士:Android Studio和VSCode的Flutter插件包括快速生成此代码的`stful`片段!
38 |
39 | ```dart
40 | //StatefulWidget的工作是接收一些数据并创建一个State类。
41 | //在本例中,我们的Widget接受一个标题,并创建一个_MyHomePageState。
42 | class MyHomePage extends StatefulWidget {
43 | final String title;
44 | MyHomePage({Key key, this.title}) : super(key:key);
45 |
46 | @override
47 | _MyHomePageState createState() => new _MyHomePageState();
48 | }
49 |
50 | //这个State类负责两件事: 保存一些数据,我们可以使用该数据更新和构建UI
51 | class _MyHomePageState extends State {
52 | // 绿色盒子是否可见
53 | bool _visible = true;
54 |
55 | @override
56 | Widget build(BuildContext context){
57 | // 绿色的盒子将与其他一些小部件一起放在这里!
58 | }
59 | }
60 | ```
61 |
62 | ## 3、显示一个按钮,以切换可见性
63 |
64 | 现在我们有了一些数据来确定我们的绿色盒子应该是可见的还是不可见的,我们需要一种更新数据的方法。在我们的例子中,如果这个盒子是可见的,我们想要隐藏它。如果盒子是隐藏的,我们要显示它!
65 |
66 | 为此,我们将显示一个按钮。当用户按下按钮时,我们会将布尔值从true转换为false,或false转换为true。我们需要使用[`setState`](https://docs.flutter.io/flutter/widgets/State/setState.html)进行此更改,它是`State`类上的一种方法。这将让Flutter知道它需要重新构建Widget。
67 |
68 | ```dart
69 | new FloatingActionButton(
70 | onPressed: (){
71 | //一定要调用setState,这将告诉Flutter根据我们的更改重建UI
72 | setState((){
73 | _visible = !_visible;
74 | })
75 | },
76 | tooltip: 'Toggle Opacity',
77 | child: new Icon(Icons.flip)
78 | )
79 | ```
80 |
81 | ## 4、添加淡入淡出动画
82 |
83 | 屏幕上有个绿色盒子。我们有一个按钮来切换可见性为真或假。那我们怎么才能让盒子淡入淡出?使用[`AnimatedOpacity`](https://docs.flutter.io/flutter/widgets/AnimatedOpacity-class.html)Widget!
84 |
85 | `AnimatedOpacity`Widget要求三个参数:
86 |
87 | - `opacity`:从0.0(不可见)到1.0(完全可见)的值
88 |
89 | - `duration`:动画应该持续多久
90 |
91 | - `child`:应用动画的部件,在我们的例子中,绿色盒子。
92 |
93 | ```dart
94 | new AnimatedOpacity(
95 | //如果Widget应该是可见的,则动画到1.0(完全可见)。如果Widget应该隐藏,动画到0.0(不可见)。
96 | opacity: _visible ? 1.0 : 0.0,
97 | duration: new Duration(milliseconds: 500),
98 | child: new Container(
99 | color: Colors.green,
100 | width: 200.0,
101 | height: 200.0
102 | )
103 | )
104 | ```
105 |
106 | ## 完整示例
107 |
108 | ```dart
109 | import 'package:flutter/material.dart';
110 |
111 | void main() => runApp(new MyApp());
112 |
113 | class MyApp extends StatelessWidget {
114 | @override
115 | Widget build(BuildContext context) {
116 | final appTitle = 'Opacity Demo';
117 | return new MaterialApp(
118 | title: appTitle,
119 | home: new MyHomePage(title: appTitle),
120 | );
121 | }
122 | }
123 |
124 | //StatefulWidget的工作是接收一些数据并创建一个State类。
125 | //在本例中,我们的Widget接受一个标题,并创建一个_MyHomePageState。
126 | class MyHomePage extends StatefulWidget {
127 | final String title;
128 |
129 | MyHomePage({Key key, this.title}) : super(key: key);
130 |
131 | @override
132 | _MyHomePageState createState() => new _MyHomePageState();
133 | }
134 |
135 | //这个State类负责两件事: 保存一些数据,我们可以使用该数据更新和构建UI
136 | class _MyHomePageState extends State {
137 | // 绿色盒子是否可见
138 | bool _visible = true;
139 |
140 | @override
141 | Widget build(BuildContext context) {
142 | return new Scaffold(
143 | appBar: new AppBar(
144 | title: new Text(widget.title),
145 | ),
146 | body: new Center(
147 | child: new AnimatedOpacity(
148 | //如果Widget应该是可见的,则动画到1.0(完全可见)。如果Widget应该隐藏,动画到0.0(不可见)。
149 | opacity: _visible ? 1.0 : 0.0,
150 | duration: new Duration(milliseconds: 500),
151 | child: new Container(
152 | width: 200.0,
153 | height: 200.0,
154 | color: Colors.green,
155 | ),
156 | ),
157 | ),
158 | floatingActionButton: new FloatingActionButton(
159 | onPressed: () {
160 | //一定要调用setState,这将告诉Flutter根据我们的更改重建UI
161 | setState(() {
162 | _visible = !_visible;
163 | });
164 | },
165 | tooltip: 'Toggle Opacity',
166 | child: new Icon(Icons.flip),
167 | ),
168 | );
169 | }
170 | }
171 | ```
172 |
173 | 
--------------------------------------------------------------------------------
/docs/design_basics/Add_a_Drawer_to_a_screen.md:
--------------------------------------------------------------------------------
1 | # 添加侧边导航
2 |
3 | 在采用Material Design设计的应用程序中,关于导航有两个主要的选择:`tab`和`drawer`。当没有足够的空间来支持`tab`,`drawer`提供了一个方便的选择。
4 |
5 | 在Flutter中,我们可以使用[`Drawer`](https://docs.flutter.io/flutter/material/Drawer-class.html)Widget与[`Scaffold`](https://docs.flutter.io/flutter/material/Scaffold-class.html)相结合,以创建一个Material Design的Drawer布局。
6 |
7 | ## 指南
8 |
9 | 1、创建`Scaffold`
10 |
11 | 2、添加一个drawer
12 |
13 | 3、向drawer中添加items
14 |
15 | 4、关闭drawer
16 |
17 | ## 1、创建`Scaffold`
18 |
19 | 为了在我们的应用程序中添加`Drawer`,我们需要将它包装在[`Scaffold`](https://docs.flutter.io/flutter/material/Scaffold-class.html)Widget中。Scaffold Widget为应用程序提供了一致的Material Design视觉结构。它还支持特殊的Material Design设计组件,如`Drawer`,`AppBar`,`SnackBar`。
20 |
21 | 在本例中,我们将创建一个带`drawer`的`Scaffold`:
22 |
23 | ```dart
24 | new Scaffold(
25 | drawer: // 我们将在下一步添加Drawer
26 | )
27 | ```
28 |
29 | ## 2、添加一个drawer
30 |
31 | 我们现在可以在`Scaffold`上加一个drawer,drawer可以是任意的Widget。但通常最好使用[material库](https://docs.flutter.io/flutter/material/material-library.html)中的`Drawer`部件,这符合Material Design设计规范。
32 |
33 | ```dart
34 | new Scaffold(
35 | drawer: new Drawer(
36 | child: // 我们将在下一步向drawer中添加item
37 | )
38 | )
39 | ```
40 |
41 | ## 3、向drawer中添加items
42 |
43 | 既然我们有了一个`Drawer`,我们就可以给它添加内容了!在本例中,我们将使用[`ListView`](https://docs.flutter.io/flutter/widgets/ListView-class.html)。虽然我们可以使用`Column`Widget,但是`ListView`更为合适,因为如果内容占用的空间超过屏幕支持的空间,它将允许用户滚动`Drawer`。
44 |
45 | 我们将使用[`DrawerHeader`](https://docs.flutter.io/flutter/material/DrawerHeader-class.html)和两个[`ListTile`](https://docs.flutter.io/flutter/material/ListTile-class.html)小部件填充`ListView`。有关使用列表的更多信息,请参见[list recipes](https://flutter.io/cookbook/#lists)。
46 |
47 | ```dart
48 | new Scaffold(
49 | drawer: new Drawer(
50 | // 添加ListView,确保了如果没有足够的垂直方向上的空间用户可以滚动
51 | child: new ListView(
52 | // 很重要,移除ListView的padding
53 | padding: EdgeInsets.zero,
54 | children: [
55 | new DrawerHeader(
56 | child: new Text('Drawer Header'),
57 | decoration: new BoxDecoration(
58 | color: Colors.blue
59 | ),
60 | ),
61 | new ListTile(
62 | title: new Text('Item 1'),
63 | onTap: (){
64 | // 更新state
65 | // ...
66 | }
67 | ),
68 | new ListTile(
69 | title: new Text('Item 2'),
70 | onTap: (){
71 | // 更新state
72 | // ...
73 | }
74 | ),
75 | ]
76 | )
77 | )
78 | )
79 | ```
80 |
81 | ## 4、关闭drawer
82 |
83 | 在用户点击某个Item后,我们通常希望关闭`drawer`。我们如何才能做到这一点?使用[`Navigator`](https://docs.flutter.io/flutter/widgets/Navigator-class.html)!
84 |
85 | 当用户打开Drawer时,Flutter会将添加Drawer到navigation堆栈中。因此,要关闭Drawer,我们可以调用`navigator.pop(context)`。
86 |
87 | ```dart
88 | new ListTile(
89 | title: new Text('Item 1'),
90 | onTap: (){
91 | // 更新state
92 | // 关闭drawer
93 | Navigator.pop(context);
94 | }
95 | )
96 | ```
97 |
98 | ## 完整示例
99 |
100 | ```dart
101 | import 'package:flutter/material.dart';
102 |
103 | void main() => runApp(new MyApp());
104 |
105 | class MyApp extends StatelessWidget {
106 | final appTitle = 'Drawer Demo';
107 | @override
108 | Widget build(BuildContext context){
109 | return new MaterialApp(
110 | title: appTitle,
111 | home: new MyHomePage(title: appTitle),
112 | );
113 | }
114 | }
115 |
116 | class MyHomePage extends StatelessWidget {
117 | final String title;
118 |
119 | MyHomePage({Key: key, this.title}) : super(key: key);
120 |
121 | @override
122 | Widget build(BuildContext context){
123 | return new Scaffold(
124 | appBar: new AppBar(
125 | title: new Text(title),
126 | ),
127 | body: new Center(
128 | child: new Text('My Page!')
129 | ),
130 | drawer: new Drawer(
131 | // 添加ListView,确保了如果没有足够的垂直方向上的空间用户可以滚动
132 | child: new ListView(
133 | // 很重要,移除ListView的padding
134 | padding: EdgeInsets.zero,
135 | children: [
136 | new DrawerHeader(
137 | child: new Text('Drawer Header'),
138 | decoration: new BoxDecoration(
139 | color: Colors.blue
140 | )
141 | ),
142 | new ListTile(
143 | title: new Text('Item 1'),
144 | onTap: (){
145 | // 更新state
146 | // 关闭drawer
147 | Navigator.pop(context);
148 | }
149 | ),
150 | new ListTile(
151 | title: new Text('Item 2'),
152 | onTap: (){
153 | // 更新state
154 | // 关闭drawer
155 | Navigator.pop(context);
156 | }
157 | )
158 | ]
159 | ),
160 | )
161 | );
162 | }
163 | }
164 | ```
165 |
166 | 
--------------------------------------------------------------------------------
/docs/design_basics/Displaying_SnackBars.md:
--------------------------------------------------------------------------------
1 | # 显示SnackBars
2 |
3 | 在某些情况下,当某些操作放生时,可以方便地简短地通知用户。例如,当用户删除列表中的消息时,我们可能希望通知他们消息已被删除。我们甚至可以给他们一个选项来撤销这个动作!
4 |
5 | 在Material Design中,这是[`SnackBar`](https://docs.flutter.io/flutter/material/SnackBar-class.html)的职责。
6 |
7 | ## 指南
8 |
9 | 1、 创建`Scaffold`
10 |
11 | 2、 显示`SnackBar`
12 |
13 | 3、 提供额外的操作
14 |
15 | ## 1、 创建`Scaffold`
16 |
17 | 当创建遵循Material Design设计准则的应用程序时,我们希望给我们的应用程序一致的视觉结构。在本例中,我们需要在屏幕底部显示`SnackBar`,而不覆盖其他重要的小部件,例如`FloatingActionButton`!
18 |
19 | [Material库](https://docs.flutter.io/flutter/material/material-library.html)中的[`Scaffold`](https://docs.flutter.io/flutter/material/Scaffold-class.html)部件为我们创建了这个可视化结构,并确保重要的小部件不重叠。
20 |
21 | ```dart
22 | new Scaffold(
23 | appBar: new AppBar(
24 | title: new Text('SnackBar Demo'),
25 | ),
26 | body: new SnackBarPage(),
27 | )
28 | ```
29 |
30 | ## 2、 显示`SnackBar`
31 |
32 | `Scaffold`创建完成之后,我们可以显示一个`SnackBar`!首先,我们需要创建一个`SnackBar`,然后使用`Scaffold`显示它。
33 |
34 | ```dart
35 | final snackBar = new SnackBar(content: new Text('Yay! A SnackBar!'));
36 |
37 | // 在部件树中找到Scaffold并用它显示SnackBar
38 | Scaffold.of(context).showSnackBar(snackBar);
39 | ```
40 |
41 | ## 3、 提供额外的操作
42 |
43 | 在某些情况下,我们可能希望在显示`SnackBar`时向用户提供附加操作。例如,如果他们无意中删除了一条消息,我们可以提供一个操作来撤销该操作。
44 |
45 | 为了实现这一点,我们可以向`SnackBar`部件提供一个`action`。
46 |
47 | ```dart
48 | final snackBar = new SnackBar(
49 | content: new Text('Yay! A SnackBar!'),
50 | action: new SnackBarAction(
51 | label: 'undo',
52 | onPressed: (){
53 | // 撤销的代码
54 | },
55 | ),
56 | );
57 | ```
58 |
59 | ## 完整示例
60 |
61 | ```dart
62 | import 'package:flutter/material.dart';
63 |
64 | void main()=>runApp(new SnackBarDemo());
65 |
66 | class SnackBarDemo extends StatelessWidget {
67 | @override
68 | Widget build(BuildContext context){
69 | return new MaterialApp(
70 | title: 'SnackBar Demo',
71 | home: new Scafflod(
72 | appBar: new AppBar(
73 | title: new Text('SnackBar Demo')
74 | ),
75 | body: new SnackBarPage(),
76 | ),
77 | );
78 | }
79 | }
80 |
81 | class SnackBarPage extends StatelessWidget {
82 | @override
83 | Widget build(BuildContext context){
84 | return new Center(
85 | child: new RaisedButton(
86 | onPressed: (){
87 | final snackBar = new SnackBar(
88 | content: new Text('Yay! A SnackBar!'),
89 | action: new SnackBarAction(
90 | label: 'undo',
91 | onPressed: (){
92 | // 撤销的代码
93 | }
94 | ),
95 | );
96 | Scaffold.of(context).showSnackBar(snackBar);
97 | },
98 | child: new Text('Show SnackBar'),
99 | ),
100 | );
101 | }
102 | }
103 | ```
104 |
105 | 
--------------------------------------------------------------------------------
/docs/design_basics/Exporting_fonts_from_a_package.md:
--------------------------------------------------------------------------------
1 | # 从包导出字体
2 |
3 | 与其将字体声明为应用程序的一部分,我们还可以将字体声明为单独包的一部分。这是一种方便的方式,可以在不同的项目之间共享字体或者将包发布到[pub website](https://pub.dartlang.org/)。
4 |
5 | ## 指南
6 |
7 | 1、将字体添加到包中
8 |
9 | 2、将包和字体添加到应用程序中
10 |
11 | 3、使用字体
12 |
13 | ## 1、将字体添加到包中
14 |
15 | 要从包导出字体,我们需要将字体文件导入到包项目的`lib`文件夹中。我们可以将字体文件直接放置在`lib`文件夹或子目录中,例如`lib/fonts`。
16 |
17 | 在本例中,我们假定我们有一个名为`awesome_package`的Flutter库,其中的字体位于`lib/fonts`文件夹中。
18 |
19 | ```
20 | awesome_package/
21 | lib/
22 | awesome_package.dart
23 | fonts/
24 | Raleway-Regular.ttf
25 | Raleway-Italic.ttf
26 | ```
27 |
28 | ## 2、将包和字体添加到应用程序中
29 |
30 | 我们现在可以使用包并使用它提供的字体,这涉及更新应用程序跟目录中的`pubspec.yaml`文件。
31 |
32 | **添加包到项目中**
33 |
34 | ```
35 | dependencies:
36 | awesome_package:
37 | ```
38 |
39 | **声明字体**
40 |
41 | 现在我们已经导入了包,我们需要告诉Flutter在哪里可以从`awesome_package`中找到字体。
42 |
43 | 要声明字体包,我们必须在字体的路径前加上`package/awesome_package`.这将告诉Flutter查看`lib`文件夹中包的字体。
44 |
45 | ```
46 | flutter:
47 | fonts:
48 | - family: Raleway
49 | fonts:
50 | - asset: packages/awesome_package/fonts/Raleway-Regular.ttf
51 | - asset: packages/awesome_package/fonts/Raleway-Italic.ttf
52 | style: italic
53 | ```
54 |
55 | ## 3、使用字体
56 |
57 | 我们可以使用[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)来更改文本的外观。要使用包字体,我们不仅需要声明要使用的字体,还需要声明字体所属的包。
58 |
59 | ```dart
60 | new Text(
61 | 'Using the Raleway font from the awesome_package',
62 | style: new TextStyle(
63 | family: 'Raleway',
64 | package: 'awesome_package',
65 | ),
66 | )
67 | ```
68 |
69 | ## 完整示例
70 |
71 | > Raleway和RobotoMono是从[Google Fonts](https://fonts.google.com/)下载的字体。
72 |
73 | **`pubspec.yaml`**
74 |
75 | ```
76 | name: package_fonts
77 | description: An example of how to use package fonts with Flutter
78 |
79 | dependencies:
80 | awesome_package:
81 | flutter:
82 | sdk: flutter
83 |
84 | dev_dependencies:
85 | flutter_test:
86 | sdk: flutter
87 |
88 | flutter:
89 | fonts:
90 | - family: Raleway
91 | fonts:
92 | - asset: packages/awesome_package/fonts/Raleway-Regular.ttf
93 | - asset: packages/awesome_package/fonts/Raleway-Italic.ttf
94 | style: italic
95 | uses-material-design: true
96 | ```
97 |
98 | **`main.dart`**
99 |
100 | ```dart
101 | import 'package:flutter/material.dart';
102 |
103 | void main() => runApp(new MyApp());
104 |
105 | class MyApp extends StatelessWidget {
106 | @override
107 | Widget build(BuildContext context){
108 | return new MaterialApp(
109 | title: 'Package Fonts',
110 | home: new MyHomePage(),
111 | );
112 | }
113 | }
114 |
115 | class MyHomePage extends StatelessWidget {
116 | @override
117 | Widget build(BuildContext context){
118 | return new Scaffold(
119 | appBar: new AppBar(
120 | title: new Text('Package Fonts')
121 | ),
122 | body: new Center(
123 | child: new Text(
124 | 'Using the Raleway font from the awesome_package',
125 | style: new TextStyle(
126 | family: 'Raleway',
127 | package: 'awesome_package'
128 | )
129 | )
130 | )
131 | );
132 | }
133 | }
134 | ```
135 |
136 | 
--------------------------------------------------------------------------------
/docs/design_basics/Updating_the_UI_based_on_orientation.md:
--------------------------------------------------------------------------------
1 | # 根据横竖屏方向更新UI
2 |
3 | 在某些情况下,当用户将屏幕从竖屏模式旋转到横屏模式时,更新应用程序的设计对用户可能会更友好。例如,我们可能希望在竖屏模式下垂直排列项目,在横屏模式中将这些项目水平排列。
4 |
5 | 在Flutter中,我们可以根据给定的[`Orientation`](https://docs.flutter.io/flutter/widgets/Orientation-class.html)构建不同的布局。在本例中,我们将构建一个列表,在竖屏模式下显示2列,在横屏模式中显示3列。
6 |
7 | ## 步骤
8 |
9 | 1、 构建一个两列的GridView
10 |
11 | 2、 使用`OrientationBuilder`改变列数
12 |
13 | ## 1. 构建一个两列的GridView
14 |
15 | 首先,我们需要使用的列表。与使用普通列表不同,我们需要一个在网格中显示项的列表。现在,我们将创建一个包含2列的网格。
16 |
17 | ```dart
18 | GridView.count(
19 | // 一个两列的网格列表
20 | crossAxisCount: 2,
21 | );
22 | ```
23 |
24 | ## 2. 使用`OrientationBuilder`改变列数
25 |
26 | 为了确定当前的方向,我们可以使用[`OrientationBuilder`](https://docs.flutter.io/flutter/widgets/OrientationBuilder-class.html)部件。`OrientationBuilder`通过比较父部件可用的宽度和高度来计算当前的方向,并在父部件的大小更改时重新构建。
27 |
28 | 使用`Orientation`,我们可以构建一个列表,在竖屏模式下显示2列,在横屏模式中显示3列。
29 |
30 | ```dart
31 | OrientationBuilder(
32 | builder: (context, orientation) {
33 | return GridView.count(
34 | crossAxisCount: orientation == Orientation.portrait ? 2 : 3,
35 | );
36 | },
37 | );
38 | ```
39 |
40 | 注:如果您对屏幕的方向感兴趣,而不是对父部件可用的空间量感兴趣,请使用`MediaQuery.of(context).orientation`,而不是`OrientationBuilder`部件。
41 |
42 | 
43 |
--------------------------------------------------------------------------------
/docs/design_basics/Using_Themes_to_share_colors_and_font_styles.md:
--------------------------------------------------------------------------------
1 | # 使用Themes共享颜色和字体样式
2 |
3 | 为了在整个应用程序中共享颜色和字体样式,我们可以利用主题。有两种方式来定义主题:使用`Theme`部件来定义应用程序特定部分的颜色和字体样式
4 | 或者应用程序范围内的颜色和字体样式。实际上,应用程序范围内的主题只是由`MaterialApp`在我们应用程序根部创建的`Theme`小部件。
5 |
6 | 在定义了主题之后,我们可以在自己的小部件里使用它。此外,由Flutter提供的Material部件将使用我们的主题来设置AppBars、Buttons、Checkboxes等等。
7 |
8 | ## 创建应用程序主题
9 |
10 | 为了在整个应用程序中共享包含颜色和字体样式的主题,我们可以向`MaterialApp`构造函数提供[`ThemeData`](https://docs.flutter.io/flutter/material/ThemeData-class.html)。
11 | 如果没有提供`theme`,Flutter将会创建一个fallback theme。
12 |
13 | ```dart
14 | new MaterialApp(
15 | title: title,
16 | theme: new ThemeData(
17 | brightness: Brightness.dark,
18 | primaryColor: Colors.lightBlue[800],
19 | accentColor: Colors.cyan[600],
20 | )
21 | )
22 | ```
23 |
24 | ```go
25 | import "fmt"
26 | func main(){
27 | fmt.Println("test")
28 | }
29 | ```
30 |
31 |
32 | 请参阅[ThemeData](https://docs.flutter.io/flutter/material/ThemeData-class.html)文档,查看所有可供定义的颜色和字体样式。
33 |
34 | ## 应用程序特定部分的主题
35 |
36 | 如果想在应用的某部分重写应用程序范围的主题,我们可以将这部分封装为`Theme`部件的子部件。
37 |
38 | 有两种方法可以解决这个问题:创建唯一的`ThemeData`,或者扩展父主题。
39 |
40 | ### 创建唯一的`ThemeData`
41 |
42 | 如果我们不想继承应用程序里现有的颜色和字体样式,可以创建一个`new ThemeData()`实例并把它传递给`Theme`部件。
43 |
44 | ```dart
45 | new Theme(
46 | // 使用"new ThemeData"创建唯一的theme
47 | data: new ThemeData(
48 | accentColor: Colors.yellow,
49 | ),
50 | child: new FloatingActionButton(
51 | onPressed: () {},
52 | child: new Icon(Icons.add),
53 | ),
54 | );
55 | ```
56 |
57 | ### 扩展父主题
58 |
59 | 相比覆盖所有内容,扩展父主题是我们更为推荐的方式。可以通过使用[`copyWith`](https://docs.flutter.io/flutter/material/ThemeData/copyWith.html)方法来实现。
60 |
61 | ```dart
62 | new Theme(
63 | data: Theme.of(context).copyWith(accentColor: Colors.yellow),
64 | child: new FloatingActionButton(
65 | onPressed: () {},
66 | child: new Icon(Icons.add),
67 | ),
68 | )
69 | ```
70 |
71 | ## 使用主题
72 |
73 | 现在我们已经定义了主题,就可以在Widget的`build`方法中通过`Theme.of(context)`函数使用它了。
74 |
75 | `Theme.of(context)`将查找Widget树并返回树中最近的`Theme`。如果我们在Widget上定义了一个独立主题,它将返回该主题。如果没有,则返回App主题。
76 |
77 | 实际上,`FloatingActionButton`使用exact技术查找`accentColor`。
78 |
79 | ```dart
80 | new Container(
81 | color: Theme.of(context).accentColor,
82 | child: new Text(
83 | 'Text with a background color',
84 | style: Theme.of(context).textTheme.title,
85 | )
86 | )
87 | ```
88 |
89 | ## 完整示例
90 |
91 | ```dart
92 | import 'package:flutter/foundation.dart';
93 | import 'package:flutter/material.dart';
94 |
95 | void main(){
96 | runApp(new MyApp());
97 | }
98 |
99 | class MyApp extends StatelessWidget {
100 | @override
101 | Widget build(BuildContext context){
102 | final appName = 'Custom Theme';
103 | return new MaterialApp(
104 | title: appName,
105 | theme: new ThemeData(
106 | brightness: Brightness.dark,
107 | primaryColor: Colors.lightBlue[800],
108 | accentColor: Colors.cyan[600],
109 | ),
110 | home: new MyHomePage(
111 | title: appName,
112 | ),
113 | );
114 | }
115 | }
116 |
117 | class MyHomePage extends StatelessWidget {
118 | final String title;
119 |
120 | MyHomePage({Key key,@required this.title}) : super(key:key);
121 |
122 | @override
123 | Widget build(BuildContext context){
124 | return new Scaffold(
125 | appBar: new AppBar(
126 | title: new Text(title),
127 | ),
128 | body: new Center(
129 | child: new Container(
130 | color: Theme.of(context).accentColor,
131 | child: new Text(
132 | 'Text with a background color',
133 | style: Theme.of(context).textTheme.title,
134 | ),
135 | ),
136 | ),
137 | floatingActionButton: new Theme(
138 | data: Theme.of(context).copyWith(accentColor: Colors.yellow),
139 | child: new FloatingActionButton(
140 | onPressed: null,
141 | child: new Icon(Icons.add),
142 | ),
143 | ),
144 | );
145 | }
146 | }
147 | ```
148 |
149 | 
--------------------------------------------------------------------------------
/docs/design_basics/Using_custom_fonts.md:
--------------------------------------------------------------------------------
1 | # 使用自定义字体
2 |
3 | 虽然Android和IOS提供高质量的系统字体,但设计者最常见的要求之一是使用自定义字体!例如,我们可以从设计人员那里定制字体,或者从[Google Fonts](https://fonts.google.com/)下载字体。
4 |
5 | Flutter对于自定义字体有着开箱即用的体验。我们可以将字体应用于整个应用程序,也可以应用于各个小部件。
6 |
7 | ## 指南
8 |
9 | 1、 导入字体文件
10 |
11 | 2、 在`pubspec.yaml`中声明字体
12 |
13 | 3、 选择一种默认字体
14 |
15 | 4、 在指定的部件中使用字体
16 |
17 | ## 1、 导入字体文件
18 |
19 | 为了使用字体,我们需要将字体文件导入到项目中。通常的做法是将字体文件放在Flutter项目的根目录中的`fonts`或`assets`文件夹中。
20 |
21 | 例如,如果我们想要将Raleway和RobotoMono字体文件导入到我们的项目中,文件夹结构将如下所示:
22 |
23 | ```
24 | awesome_app/
25 | fonts/
26 | Raleway-Regular.ttf
27 | Raleway-Italic.ttf
28 | RobotoMono-Regular.ttf
29 | RobotoMono-Bold.ttf
30 | ```
31 |
32 | ## 2、 在`pubspec.yaml`中声明字体
33 |
34 | 现在我们项目中有了字体文件,还需要告诉Flutter在哪里找到它。我们可以通过在`pubspec.yaml`中包含一个字体定义来做到这一点。
35 |
36 | ```
37 | flutter:
38 | fonts:
39 | - family: Raleway
40 | fonts:
41 | - asset: fonts/Raleway-Regular.ttf
42 | - asset: fonts/Raleway-Italic.ttf
43 | style: italic
44 | - family: RobotoMono
45 | fonts:
46 | - asset: fonts/RobotoMono-Regular.ttf
47 | - asset: fonts/RobotoMono-Bold.ttf
48 | weight: 700
49 | ```
50 |
51 | **`pubspec.yaml`选项说明**
52 |
53 | `family`决定字体的名称,我们可以在[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)对象的[`fontFamily`](https://docs.flutter.io/flutter/painting/TextStyle/fontFamily.html)属性中使用该字体。
54 |
55 | `asset`是字体文件的路径,相对于`pubspec.yaml`文件。这些文件包含字体中的文字的概述。在构建应用程序时,这些文件包含在应用程序的静态资源包中。
56 |
57 | 单一字体可以引用具有不同字重和样式的许多不同文件:
58 | - `weight`属性将文件中的字重指定为100到900之间的整数倍数。这些值对应于[`FontWight`](https://docs.flutter.io/flutter/dart-ui/FontWeight-class.html),可以在[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)对象的[`fontWight`](https://docs.flutter.io/flutter/painting/TextStyle/fontWeight.html)属性中使用。
59 | - `style`属性指定文件中的字体是斜体还是正常。这些值对应于[`FontStyle`](https://docs.flutter.io/flutter/dart-ui/FontStyle-class.html),可以在[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)对象的[`fontStyle`](https://docs.flutter.io/flutter/painting/TextStyle/fontStyle.html)属性中使用。
60 |
61 | ## 3、 选择一种默认字体
62 |
63 | 对于如何将字体应用于文本,我们有两个选项:默认字体或仅在特定部件中。
64 |
65 | 要使用字体作为默认设置,我们可以将`fontFamily`属性设置为应用程序`theme`的一部分。我们提供给`fontFamily`的值必须与在`pubspec.yaml`中声明的名字相匹配。
66 |
67 | ```dart
68 | new MaterialApp(
69 | title: 'Custom Fonts',
70 | theme: new ThemeData(fontFamily: 'Raleway'),
71 | home: new MyHomePage(),
72 | );
73 | ```
74 |
75 | ## 4、 在指定的部件中使用字体
76 |
77 | 如果我们想将字体应用到特定的Widget,比如`Text`Widget,我们可以为Widget提供一个[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)。
78 |
79 | 在本例中,我们将将RobotoMono字体应用于单个`Text`Widget。同样,`fontFamily`必须与我们在`pubspec.yaml`中的声明相匹配。
80 |
81 | ```dart
82 | new Text(
83 | 'Roboto Mono sample',
84 | style: new TextStyle(fontFamily: 'RobotoMono')
85 | );
86 | ```
87 |
88 | #### TextStyle
89 |
90 | 如果[`TextStyle`](https://docs.flutter.io/flutter/painting/TextStyle-class.html)对象指定的是没有确切字体文件的字重或样式,则引擎将使用该字体的一个更通用的文件,并试图推断所请求的字重和样式。
91 |
92 | ## 完整示例
93 |
94 | > Raleway和RobotoMono是从[Google Fonts](https://fonts.google.com/)下载的字体。
95 |
96 | **`pubspec.yaml`**
97 |
98 | ```
99 | name: custom_fonts
100 | description: An example of how to use custom fonts with Flutter
101 |
102 | dependencies:
103 | flutter:
104 | sdk: flutter
105 |
106 | dev_dependencies:
107 | flutter_test:
108 | sdk: flutter
109 |
110 | flutter:
111 | fonts:
112 | - family: Raleway
113 | fonts:
114 | - asset: fonts/Raleway-Regular.ttf
115 | - asset: fonts/Raleway-Italic.ttf
116 | style: italic
117 | - family: RobotoMono
118 | fonts:
119 | - asset: fonts/RobotoMono-Regular.ttf
120 | - asset: fonts/RobotoMono-Bold.ttf
121 | weight: 700
122 | uses-material-design: true
123 | ```
124 |
125 | **`main.dart`**
126 |
127 | ```dart
128 | import 'package:flutter/material.dart';
129 |
130 | void main() => runApp(new MyApp());
131 |
132 | class MyApp extends StatelessWidget {
133 | @override
134 | Widget build(BuildContext context) {
135 | return new MaterialApp(
136 | title: 'Custom Fonts',
137 | // 设置Raleway为默认字体
138 | theme: new ThemeData(fontFamily: 'Raleway'),
139 | home: new MyHomePage(),
140 | );
141 | }
142 | }
143 |
144 | class MyHomePage extends StatelessWidget {
145 | @override
146 | Widget build(BuildContext context) {
147 | return new Scaffold(
148 | // AppBar将会使用Raleway默认字体
149 | appBar: new AppBar(title: new Text('Custom Fonts')),
150 | body: new Center(
151 | // 这个Text将会使用RobotoMono字体
152 | child: new Text(
153 | 'Roboto Mono sample',
154 | style: new TextStyle(fontFamily: 'RobotoMono'),
155 | ),
156 | ),
157 | );
158 | }
159 | }
160 | ```
161 |
162 | 
--------------------------------------------------------------------------------
/docs/design_basics/Working_with_Tabs.md:
--------------------------------------------------------------------------------
1 | # 使用Tabs
2 |
3 | 使用选项卡是应用程序中遵循Material Design设计准则的常见模式。Flutter包括一种方便的方法来创建tab布局作为[Material库](https://docs.flutter.io/flutter/material/material-library.html)的一部分。
4 |
5 | ## 指南
6 |
7 | 1、 创建一个`TabController`
8 |
9 | 2、 创建Tabs
10 |
11 | 3、 为每个tab创建内容
12 |
13 | ## 1、 创建一个`TabController`
14 |
15 | 为了使选项卡工作,我们需要保持选定的选项卡和内容部分保持同步。这是[`TabController`](https://docs.flutter.io/flutter/material/TabController-class.html)的职责。
16 |
17 | 我们可以手动创建`TabController`或使用[`DefaultTabController`](https://docs.flutter.io/flutter/material/DefaultTabController-class.html)。使用`DefaultTabController`是最简单的选择。它将为我们创建一个`TabController`,并将其提供给所有的后代小部件。
18 |
19 | ```dart
20 | new DefaultTabController(
21 | length: 3, // 需要显示的选项卡数量
22 | child: // 看下一步
23 | );
24 | ```
25 |
26 | ## 2、 创建Tabs
27 |
28 | 现在我们有了一个`TabController`,我们可以使用[`TabBar`](https://docs.flutter.io/flutter/material/TabController-class.html)创建我们的选项卡了。在本例中,我们将创建一个带有3个[`Tab`](https://docs.flutter.io/flutter/material/Tab-class.html)小部件的`TabBar`,并将其放置在[`AppBar`](https://docs.flutter.io/flutter/material/AppBar-class.html)中。
29 |
30 | ```dart
31 | new DefaultTabController(
32 | length: 3,
33 | child: new Scaffold(
34 | appBar: new AppBar(
35 | bottom: new TabBar(
36 | tabs: [
37 | new Tab(icon: new Icon(Icons.directions_car)),
38 | new Tab(icon: new Icon(Icons.directions_transit)),,
39 | new Tab(icon: new Icon(Icons.directions_bike)),
40 | ]
41 | ),
42 | )
43 | ),
44 | );
45 | ```
46 |
47 | 默认情况下,`TabBar`查找最近的`DefaultTabController`的Widget树。如果要手动创建`TabController`,则需要将其传递给`TabBar`。
48 |
49 | ## 3、 为每个tab创建内容
50 |
51 | 现在我们有了选项卡,我们希望在选择选项卡时显示内容。为了这个目的,我们将使用[`TabBarView`](https://docs.flutter.io/flutter/material/TabBarView-class.html)部件。
52 |
53 | **注意**:顺序很重要,必须与`TabBar`中选项卡的顺序相对应!
54 |
55 | ```dart
56 | new TabBarView(
57 | children: [
58 | new Icon(Icons.directions_car),
59 | new Icon(Icons.directions_transit),
60 | new Icon(Icons.directions_bike),
61 | ]
62 | )
63 | ```
64 |
65 | ## 完整示例
66 |
67 | ```dart
68 | import 'package:flutter/material.dart';
69 |
70 | void main() => runApp(new TabBarDemo());
71 |
72 | class TabBarDemo extends StatelessWidget {
73 | @override
74 | Widget build(BuildContext context){
75 | return new MaterialApp(
76 | title: 'TabBar Demo',
77 | home: new DefaultTabBarController(
78 | length: 3,
79 | child: new Scaffold(
80 | appBar: new AppBar(
81 | title: new Text('Tabs Demo'),
82 | bottom: new TabBar(
83 | tabs: [
84 | new Tab(icon: new Icon(Icons.directions_car)),
85 | new Tab(icon: new Icon(Icons.directions_transit)),
86 | new Tab(icon: new Icon(Icons.directions_bike)),
87 | ],
88 | )
89 | ),
90 | body: new TabBarView(
91 | children: [
92 | new Icon(Icons.directions_car),
93 | new Icon(Icons.directions_transit),
94 | new Icon(Icons.directions_bike),
95 | ]
96 | )
97 | )
98 | )
99 | );
100 | }
101 | }
102 | ```
103 |
104 | 
--------------------------------------------------------------------------------
/docs/forms/Building_a_form_with_validation.md:
--------------------------------------------------------------------------------
1 | # 使用验证构建表单
2 |
3 | 应用程序经常需要用户再文本框中输入信息。比如,我们可能正在开发一个应用程序,它要求我们的用户使用email和密码来登录。
4 |
5 | 为了使我们的程序更加安全和易用,我们可以检查用户提供的信息是否有效。如果用户在表单里填写了正确的信息,我们就可以提交这些信息。但如果用户提交了不正确的信息,我们可以显示一个友好的错误提示来告诉用户哪里出错了。
6 |
7 | 在这个例子中,我们将看到如何用单个文本字段向表单添加验证。
8 |
9 | ## 步骤
10 |
11 | 1、 创建一个带有`GlobalKey`的`Form`
12 |
13 | 2、 添加一个有验证逻辑的`TextFormField`
14 |
15 | 3、 在表单中添加一个验证和提交表单的按钮
16 |
17 | ## 1. 创建一个带有`GlobalKey`的`Form`
18 |
19 | 首先,我们需要一个[Form](https://docs.flutter.io/flutter/widgets/Form-class.html)。Form部件充当一个容器来对多个表单域进行分组和验证。
20 |
21 | 创建了form之后,我们同时需要提供一个[GlobalKey](https://docs.flutter.io/flutter/widgets/GlobalKey-class.html)。这将唯一地标识我们正在使用的表单,并允许我们在稍后的步骤中验证表单。
22 |
23 | ```dart
24 | // 定义自定义的Form部件
25 | class MyCustomForm extends StatefulWidget {
26 | @override
27 | MyCustomFormState createState() {
28 | return MyCustomFormState();
29 | }
30 | }
31 |
32 | // 定义相应的状态类。这个类将保存与表单相关的数据
33 | class MyCustomFormState extends State {
34 | // 创建GlobalKey
35 | // 注意: 这是 `GlobalKey`, 不是 GlobalKey!
36 | final _formKey = GlobalKey();
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | // 使用上面创建的_formKey构建一个表单小部件
41 | return Form(
42 | key: _formKey,
43 | child: // 我们将在接下来的步骤中建立子部件
44 | );
45 | }
46 | }
47 | ```
48 |
49 | ## 2. 添加一个有验证逻辑的`TextFormField`
50 |
51 | 现在我们已经有了Form表单,但我们还没有为用户提供输入文本的方式。这正是[TextFormField](https://docs.flutter.io/flutter/material/TextFormField-class.html)要做的。`TextFormField`部件渲染了Material Design的文本输入框,并知道如何在出现验证错误时显示验证错误。
52 |
53 | 如何验证输入呢?通过向`TextFormField`提供`validator`函数。如果用户提供的信息出现错误,`validator`函数必须返回包含错误消息的字符串。如果没有错误,则函数不应返回任何内容。
54 |
55 | 在本例中,我们将创建一个`validator`,以确保TextFormField不为空。如果它是空的,我们将返回一个友好的错误消息。
56 |
57 | ```dart
58 | TextFormField(
59 | // validator接受用户输入的文本作为参数
60 | validator: (value) {
61 | if (value.isEmpty) {
62 | return 'Please enter some text';
63 | }
64 | },
65 | );
66 | ```
67 |
68 | ## 3. 在表单中添加一个验证和提交表单的按钮
69 |
70 | 现在我们有了一个带有文本字段的表单,我们需要提供一个按钮,用户可以点击这个按钮来提交信息。
71 |
72 | 当用户试图提交表单时,我们需要检查表单是否有效。如果是的话,我们将展示一个成功的信息。如果文本字段没有内容,我们需要显示错误消息。
73 |
74 | ```dart
75 | RaisedButton(
76 | onPressed: () {
77 | // 如果表单有效,验证将返回true,如果表单无效,则返回false。
78 | if (_formKey.currentState.validate()) {
79 | // 如果表单有效,则显示一个snackbar。
80 | // 在现实世界中,您通常希望调用服务器或将信息保存在数据库中
81 | Scaffold
82 | .of(context)
83 | .showSnackBar(SnackBar(content: Text('Processing Data')));
84 | }
85 | },
86 | child: Text('Submit'),
87 | );
88 | ```
89 |
90 | ## 它是如何工作的
91 |
92 | 为了验证表单,我们需要使用步骤1中创建的`_formKey`。我们可以使用`_formKey.currentState`方法来访问[FormState](https://docs.flutter.io/flutter/widgets/FormState-class.html),在构建表单时,`FormState`是由Flutter自动创建的。
93 |
94 | `FormState`类包含`validate`方法。当调用`validate`方法时,它将为表单中的每个文本字段运行`validator`函数。如果一切正常,该方法将返回true。如果任何文本字段包含错误,它将显示每个无效文本字段的错误消息并返回false。
95 |
96 | ## 完整示例
97 |
98 | ```dart
99 | import 'package:flutter/material.dart';
100 |
101 | void main() => runApp(MyApp());
102 |
103 | class MyApp extends StatelessWidget {
104 | @override
105 | Widget build(BuildContext context) {
106 | final appTitle = 'Form Validation Demo';
107 |
108 | return MaterialApp(
109 | title: appTitle,
110 | home: Scaffold(
111 | appBar: AppBar(
112 | title: Text(appTitle),
113 | ),
114 | body: MyCustomForm(),
115 | ),
116 | );
117 | }
118 | }
119 |
120 | // 创建Form部件
121 | class MyCustomForm extends StatefulWidget {
122 | @override
123 | MyCustomFormState createState() {
124 | return MyCustomFormState();
125 | }
126 | }
127 |
128 | // 定义相应的状态类。这个类将保存与表单相关的数据
129 | class MyCustomFormState extends State {
130 | // 创建GlobalKey
131 | // 注意: 这是 `GlobalKey`, 不是 GlobalKey!
132 | final _formKey = GlobalKey();
133 |
134 | @override
135 | Widget build(BuildContext context) {
136 | // 使用上面创建的_formKey构建一个表单小部件
137 | return Form(
138 | key: _formKey,
139 | child: Column(
140 | crossAxisAlignment: CrossAxisAlignment.start,
141 | children: [
142 | TextFormField(
143 | validator: (value) {
144 | if (value.isEmpty) {
145 | return 'Please enter some text';
146 | }
147 | },
148 | ),
149 | Padding(
150 | padding: const EdgeInsets.symmetric(vertical: 16.0),
151 | child: RaisedButton(
152 | onPressed: () {
153 | // 如果表单有效,则显示一个snackbar。
154 | // 在现实世界中,您通常希望调用服务器或将信息保存在数据库中
155 | if (_formKey.currentState.validate()) {
156 | // If the form is valid, we want to show a Snackbar
157 | Scaffold.of(context)
158 | .showSnackBar(SnackBar(content: Text('Processing Data')));
159 | }
160 | },
161 | child: Text('Submit'),
162 | ),
163 | ),
164 | ],
165 | ),
166 | );
167 | }
168 | }
169 | ```
170 |
171 | 
172 |
--------------------------------------------------------------------------------
/docs/forms/Create_and_styled_a_text_field.md:
--------------------------------------------------------------------------------
1 | # 创建文本字段并添加样式
2 |
3 | 文本字段允许用户在我们的应用程序中键入文本。文本字段可以用来构建表单、消息输入框、搜索框等等!在这个章节中,我们将探讨如何创建文本字段并添加样式。Flutter提供了两个文本字段:[TextField](https://docs.flutter.io/flutter/material/TextField-class.html)和[TextFormField](https://docs.flutter.io/flutter/material/TextFormField-class.html)。
4 |
5 | ## `TextField`
6 |
7 | `TextField`是最常用的文本输入部件。默认情况下,`TextField`使用下划线修饰。通过提供[`InputDecoration`](https://docs.flutter.io/flutter/material/InputDecoration-class.html)作为`TextField`的[`decoration`](https://docs.flutter.io/flutter/material/TextField/decoration.html)属性,我们可以添加标签(`label`)、图标(`icon`)、内联提示文本(`hintText`)和错误文本(`errorText`)。若要完全删除装饰(包括下划线和为标签保留的空间),请显式地将`decoration`设置为空。
8 |
9 | ```dart
10 | TextField(
11 | decoration: InputDecoration(
12 | border: InputBorder.none,
13 | hintText: 'Please enter a search term'
14 | ),
15 | );
16 | ```
17 |
18 | ## `TextFormField`
19 |
20 | `TextFormField`封装一个`TextField`并将其与封闭表单集成。这提供了其他功能,例如验证和与其他[`FormField`](https://docs.flutter.io/flutter/widgets/FormField-class.html)部件的集成。
21 |
22 | ```dart
23 | TextFormField(
24 | decoration: InputDecoration(
25 | labelText: 'Enter your username'
26 | ),
27 | );
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/forms/Focus_on_a_text_field.md:
--------------------------------------------------------------------------------
1 | # 使TextField获得焦点
2 |
3 | 当选择文本字段并接受输入时,通常会获得焦点(“focus”),用户可以通过点击它们来聚焦文本字段,开发人员可以使用本章中描述的工具来聚焦文本字段。
4 |
5 | 管理焦点是创建具有简便操作流的表单的基本工具。例如,我们有一个带有文本字段的搜索屏幕。当用户导航到搜索屏幕时,我们可以将搜索项文本字段聚焦。这允许用户在屏幕可见时立即开始键入,而无需手动点击文本字段!
6 |
7 | 在本章中,我们将学习如何在文本字段可见时就获得焦点,以及如何在点击按钮时聚焦文本字段。
8 |
9 | ## 在文本字段可见时就获得焦点
10 |
11 | 为了使文本字段在可见时立即对焦,我们可以使用“`autofocus`”属性。
12 |
13 | ```dart
14 | TextField(
15 | autofocus: true,
16 | );
17 | ```
18 |
19 | ## 在点击按钮时聚焦文本字段
20 |
21 | 我们可能需要在特定的时间点聚焦特定的文本字段,而不是立即聚焦特定的文本字段。在本例中,我们将看到用户按下按钮后如何聚焦文本字段。在现实世界中,您还可能需要根据API调用或验证错误来聚焦特定的文本字段。
22 |
23 | ### 步骤
24 |
25 | 1、 创建`FocusNode`
26 |
27 | 2、 将`FocusNode`传递给一个`TextField`
28 |
29 | 3、 当点击被按钮时聚焦`TextField`
30 |
31 | ### 1、 创建`FocusNode`
32 |
33 | 首先,我们需要创建一个[FocusNode](https://docs.flutter.io/flutter/widgets/FocusNode-class.html)。我们将使用`FocusNode`来识别Flutter的“焦点树”中的特定`TextField`。这将使我们能够在接下来的步骤中聚焦TextField。
34 |
35 | 由于FocusNode是长期存在的对象,我们需要使用`State`类来管理生命周期。为此,在`State`类的`initState`方法中创建`FocusNode`实例,并在`dispose`方法中清除它们。
36 |
37 | ```dart
38 | class MyCustomForm extends StatefulWidget {
39 | @override
40 | _MyCustomFormState createState() => _MyCustomFormState();
41 | }
42 |
43 | class _MyCustomFormState extends State {
44 | // 定义FocusNode。为了管理生命周期,在initState中创建FocusNode,在dispose中清除FocusNode
45 | FocusNode myFocusNode;
46 |
47 | @override
48 | void initState() {
49 | super.initState();
50 |
51 | myFocusNode = FocusNode();
52 | }
53 |
54 | @override
55 | void dispose() {
56 | // 当Form dispose的时候清除focusNode
57 | myFocusNode.dispose();
58 |
59 | super.dispose();
60 | }
61 |
62 | @override
63 | Widget build(BuildContext context) {
64 | // 下一步中完成
65 | }
66 | }
67 | ```
68 |
69 | ### 2. 将`FocusNode`传递给一个`TextField`
70 |
71 | 现在我们有了`FocusNode`,我们可以将它传递给`build`方法中特定的`TextField`。
72 |
73 | ```dart
74 | class _MyCustomFormState extends State {
75 |
76 | @override
77 | Widget build(BuildContext context) {
78 | return TextField(
79 | focusNode: myFocusNode,
80 | );
81 | }
82 | }
83 | ```
84 |
85 | ### 3. 当点击被按钮时聚焦`TextField`
86 |
87 | 最后,当用户点击按钮时,我们想要聚焦文本字段!我们将使用[`requestFocus`](https://docs.flutter.io/flutter/widgets/FocusScopeNode/requestFocus.html)方法来完成此任务。
88 |
89 | ```dart
90 | FloatingActionButton(
91 | // 当按下按钮时,flutter使用myFocusNode将文本字段聚焦。
92 | onPressed: () => FocusScope.of(context).requestFocus(myFocusNode),
93 | );
94 | ```
95 |
96 | ### 完整示例
97 |
98 | ```dart
99 | import 'package:flutter/material.dart';
100 |
101 | void main() => runApp(MyApp());
102 |
103 | class MyApp extends StatelessWidget {
104 | @override
105 | Widget build(BuildContext context) {
106 | return MaterialApp(
107 | title: 'Text Field Focus',
108 | home: MyCustomForm(),
109 | );
110 | }
111 | }
112 |
113 | class MyCustomForm extends StatefulWidget {
114 | @override
115 | _MyCustomFormState createState() => _MyCustomFormState();
116 | }
117 |
118 | class _MyCustomFormState extends State {
119 | // 定义FocusNode。为了管理生命周期,在initState中创建FocusNode,在dispose中清除FocusNode
120 | FocusNode myFocusNode;
121 |
122 | @override
123 | void initState() {
124 | super.initState();
125 |
126 | myFocusNode = FocusNode();
127 | }
128 |
129 | @override
130 | void dispose() {
131 | // 当Form dispose的时候清除focusNode
132 | myFocusNode.dispose();
133 |
134 | super.dispose();
135 | }
136 |
137 | @override
138 | Widget build(BuildContext context) {
139 | return Scaffold(
140 | appBar: AppBar(
141 | title: Text('Text Field Focus'),
142 | ),
143 | body: Padding(
144 | padding: const EdgeInsets.all(16.0),
145 | child: Column(
146 | children: [
147 | // The first text field will be focused as soon as the app starts
148 | TextField(
149 | autofocus: true,
150 | ),
151 | // The second text field will be focused when a user taps on the
152 | // FloatingActionButton
153 | TextField(
154 | focusNode: myFocusNode,
155 | ),
156 | ],
157 | ),
158 | ),
159 | floatingActionButton: FloatingActionButton(
160 | onPressed: () => FocusScope.of(context).requestFocus(myFocusNode),
161 | tooltip: 'Focus Second Text Field',
162 | child: Icon(Icons.edit),
163 | ),
164 | );
165 | }
166 | }
167 | ```
168 |
169 | 
170 |
171 |
--------------------------------------------------------------------------------
/docs/forms/Retrieve_the_value_of_a_text_field.md:
--------------------------------------------------------------------------------
1 | # 获取文本字段的值
2 |
3 | 在本章中,我们将看到如何获取用户在文本字段中键入的文本。
4 |
5 | ## 步骤
6 |
7 | 1、 创建一个`TextEditingController`
8 |
9 | 2、 将`TextEditingController`应用到一个`TextField`中
10 |
11 | 3、 显示文本字段的当前值
12 |
13 | ## 1. 创建一个`TextEditingController`
14 |
15 | 为了获取用户输入文本字段的文本,我们需要创建一个[`TextEditingController`](https://docs.flutter.io/flutter/widgets/TextEditingController-class.html)。然后,我们将在接下来的步骤中将`TextEditingControlle`提供给`TextField`。
16 |
17 | 一旦向`TextField`或`TextFormField`提供了`TextEditingController`,我们就可以使用它获取用户在该文本字段中键入的文本。
18 |
19 | > 注意:当不再需要`TextEditingController`时,请记住释放(`dispose`)它。这将确保我们丢弃对象使用的任何资源。
20 |
21 | ```dart
22 | class MyCustomForm extends StatefulWidget {
23 | @override
24 | _MyCustomFormState createState() => _MyCustomFormState();
25 | }
26 |
27 | class _MyCustomFormState extends State {
28 | // 创建文本控制器。我们将使用它来接收TextField的当前值
29 | final myController = TextEditingController();
30 |
31 | @override
32 | void dispose() {
33 | // 当Widget从Widget树中移除时,清除控制器
34 | myController.dispose();
35 | super.dispose();
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | // 下一步中完成
41 | }
42 | }
43 | ```
44 |
45 | ## 2. 将`TextEditingController`应用到一个`TextField`中
46 |
47 | 现在我们有了要使用的`TextEditingController`,我们需要将其连接到特定的文本字段。为此,我们将`TextEditingController`提供给`TextField`或`TextFormField`部件作为`controller`属性。
48 |
49 | ```dart
50 | TextField(
51 | controller: myController,
52 | );
53 | ```
54 |
55 | ## 3. 显示文本字段的当前值
56 |
57 | 在向文本字段提供了`TextEditingController`之后,我们就可以开始读取值了!我们将使用`TextEditingController`提供的[`text`](https://docs.flutter.io/flutter/widgets/TextEditingController/text.html)方法来获取用户在文本字段中键入的文本字符串。
58 |
59 | 在本例中,当用户点击操作按钮时,我们将使用文本字段的当前值显示一个alert对话框。
60 |
61 | ```dart
62 | FloatingActionButton(
63 | // 当用户点击操作按钮时,使用文本字段的当前值显示一个alert对话框。
64 | onPressed: () {
65 | return showDialog(
66 | context: context,
67 | builder: (context) {
68 | return AlertDialog(
69 | // 使用`TextEditingController.text`获取用户输入的值
70 | content: Text(myController.text),
71 | );
72 | },
73 | );
74 | },
75 | tooltip: 'Show me the value!',
76 | child: Icon(Icons.text_fields),
77 | );
78 | ```
79 |
80 | ## 完整示例
81 |
82 | ```dart
83 | import 'package:flutter/material.dart';
84 |
85 | void main() => runApp(MyApp());
86 |
87 | class MyApp extends StatelessWidget {
88 | @override
89 | Widget build(BuildContext context) {
90 | return MaterialApp(
91 | title: 'Retrieve Text Input',
92 | home: MyCustomForm(),
93 | );
94 | }
95 | }
96 |
97 | class MyCustomForm extends StatefulWidget {
98 | @override
99 | _MyCustomFormState createState() => _MyCustomFormState();
100 | }
101 |
102 | class _MyCustomFormState extends State {
103 | // 创建文本控制器。我们将使用它来接收TextField的当前值
104 | final myController = TextEditingController();
105 |
106 | @override
107 | void dispose() {
108 | // 当Widget从Widget树中移除时,清除控制器
109 | myController.dispose();
110 | super.dispose();
111 | }
112 |
113 | @override
114 | Widget build(BuildContext context) {
115 | return Scaffold(
116 | appBar: AppBar(
117 | title: Text('Retrieve Text Input'),
118 | ),
119 | body: Padding(
120 | padding: const EdgeInsets.all(16.0),
121 | child: TextField(
122 | controller: myController,
123 | ),
124 | ),
125 | floatingActionButton: FloatingActionButton(
126 | // 当用户点击操作按钮时,使用文本字段的当前值显示一个alert对话框。
127 | onPressed: () {
128 | return showDialog(
129 | context: context,
130 | builder: (context) {
131 | return AlertDialog(
132 | // 使用`TextEditingController.text`获取用户输入的值
133 | content: Text(myController.text),
134 | );
135 | },
136 | );
137 | },
138 | tooltip: 'Show me the value!',
139 | child: Icon(Icons.text_fields),
140 | ),
141 | );
142 | }
143 | }
144 | ```
145 |
146 | 
147 |
--------------------------------------------------------------------------------
/docs/forms/handling_changes_to_a_text_field.md:
--------------------------------------------------------------------------------
1 | # 处理文本字段的更改
2 |
3 | 在某些情况下,每当文本字段中的文本发生变化后运行回调函数是很常见的。例如,我们可能希望构建一个具有自动完成功能的搜索界面。在这种情况下,我们希望根据用户的输入来更新搜索结果。
4 |
5 | 如何在每次文本更改时运行回调函数?对于Flutter,我们有两个选择:
6 |
7 | 1. 在`TextField`中提供一个`onChanged`回调函数
8 | 2. 使用`TextEditingController`
9 |
10 | ## 1. 在`TextField`中提供一个`onChanged`回调函数
11 |
12 | 最简单的方法是向[`TextField`](https://docs.flutter.io/flutter/material/TextField-class.html)提供[`onChanged`](https://docs.flutter.io/flutter/material/TextField/onChanged.html)回调。每当文本更改时,将调用回调。这种方法的一个缺点是它不能与`TextFormField`部件一起工作。
13 |
14 | 在本例中,每次文本更改时,我们都会将文本字段的当前值打印到控制台。
15 |
16 | ```dart
17 | TextField(
18 | onChanged: (text) {
19 | print("First text field: $text");
20 | },
21 | );
22 | ```
23 |
24 | ## 2. 使用`TextEditingController`
25 |
26 | 一种更强大但更精细的方法是提供[`TextEditingController`](https://docs.flutter.io/flutter/widgets/TextEditingController-class.html)作为`TextField`或`TextFormField`的[`controller`](https://docs.flutter.io/flutter/material/TextField/controller.html)属性。
27 |
28 | 要在文本更改时得到通知,我们可以使用其[`addListener`](https://docs.flutter.io/flutter/foundation/ChangeNotifier/addListener.html)方法侦听控制器。
29 |
30 | ### 步骤
31 |
32 | 1、 创建一个`TextEditingController`
33 |
34 | 2、 将`TextEditingController`应用到一个`TextField`中
35 |
36 | 3、 创建一个方法来打印文本字段的值
37 |
38 | 4、 侦听控制器(controller)的更改
39 |
40 | ### 1. 创建一个`TextEditingController`
41 |
42 | 首先,我们需要创建一个`TextEditingController`。在接下来的步骤中,我们将向`TextField`提供`TextEditingController`。一旦我们将这两个类连接在一起,我们就可以监听文本字段的更改了!
43 |
44 | ```dart
45 | class MyCustomForm extends StatefulWidget {
46 | @override
47 | _MyCustomFormState createState() => _MyCustomFormState();
48 | }
49 |
50 | class _MyCustomFormState extends State {
51 | // 创建文本控制器。我们将使用它来接收TextField的当前值
52 | final myController = TextEditingController();
53 |
54 | @override
55 | void dispose() {
56 | // 当Widget从Widget树中移除时,清除控制器
57 | myController.dispose();
58 | super.dispose();
59 | }
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | // 下一步中完成
64 | }
65 | }
66 | ```
67 |
68 | > 注意:当不再需要`TextEditingController`时,请记住释放(`dispose`)它。这将确保我们丢弃对象使用的任何资源。
69 |
70 | ### 2. 将`TextEditingController`应用到一个`TextField`中
71 |
72 | 我们还必须向`TextField`或`TextFormField`提供`TextEditingController`。一旦连接好,我们就可以开始监听文本字段的更改。
73 |
74 | ```dart
75 | TextField(
76 | controller: myController,
77 | );
78 | ```
79 |
80 | ### 3. 创建一个方法来打印文本字段的值
81 |
82 | 现在,我们需要一个每次文本更改时都应该运行的函数。在本例中,我们将创建一个打印文本字段当前值的方法。
83 |
84 | 此方法将留在`_MyCustomFormState`类中。
85 |
86 | ```dart
87 | _printLatestValue() {
88 | print("Second text field: ${myController.text}");
89 | }
90 | ```
91 |
92 | ### 4. 侦听控制器(controller)的更改
93 |
94 | 最后,当文本发生变化时,我们需要侦听`TextEditingController`并运行`_printLatestValue`方法。我们将使用`addListener`方法来完成此任务。
95 |
96 | 在本例中,我们将在`_MyCustomFormState`类初始化时开始侦听文本字段的更改,并在释放`_MyCustomFormState`时停止侦听。
97 |
98 | ```dart
99 | class _MyCustomFormState extends State {
100 | @override
101 | void initState() {
102 | super.initState();
103 |
104 | // 开始侦听更改
105 | myController.addListener(_printLatestValue);
106 | }
107 | }
108 | ```
109 |
110 | ### 完整示例
111 |
112 | ```dart
113 | import 'package:flutter/material.dart';
114 |
115 | void main() => runApp(MyApp());
116 |
117 | class MyApp extends StatelessWidget {
118 | @override
119 | Widget build(BuildContext context) {
120 | return MaterialApp(
121 | title: 'Retrieve Text Input',
122 | home: MyCustomForm(),
123 | );
124 | }
125 | }
126 | lass MyCustomForm extends StatefulWidget {
127 | @override
128 | _MyCustomFormState createState() => _MyCustomFormState();
129 | }
130 |
131 | class _MyCustomFormState extends State {
132 | // 创建文本控制器。我们将使用它来接收TextField的当前值
133 | final myController = TextEditingController();
134 |
135 | @override
136 | void initState() {
137 | super.initState();
138 |
139 | // 开始侦听更改
140 | myController.addListener(_printLatestValue);
141 | }
142 |
143 | @override
144 | void dispose() {
145 | // 当Widget从Widget树中移除时,清除控制器
146 | myController.dispose();
147 | super.dispose();
148 | }
149 |
150 | _printLatestValue() {
151 | print("Second text field: ${myController.text}");
152 | }
153 |
154 | @override
155 | Widget build(BuildContext context) {
156 | return Scaffold(
157 | appBar: AppBar(
158 | title: Text('Retrieve Text Input'),
159 | ),
160 | body: Padding(
161 | padding: const EdgeInsets.all(16.0),
162 | child: Column(
163 | children: [
164 | TextField(
165 | onChanged: (text) {
166 | print("First text field: $text");
167 | },
168 | ),
169 | TextField(
170 | controller: myController,
171 | ),
172 | ],
173 | ),
174 | ),
175 | );
176 | }
177 | }
178 | ```
179 |
180 |
181 |
--------------------------------------------------------------------------------
/docs/handling_gestures/Adding_Material_Touch_ripples.md:
--------------------------------------------------------------------------------
1 | # 添加Material波纹
2 |
3 | 在设计一个应该遵循Material设计准则的应用程序时,我们想要在点击时将波纹动画添加到小部件中。
4 |
5 | Flutter提供了[`InkWell`](https://docs.flutter.io/flutter/material/InkWell-class.html)Widget来实现这一效果。
6 |
7 | ## 指南
8 |
9 | 1、创建想要点击的组件
10 |
11 | 2、将其包装在`InkWell`中管理`Tap`回调和波纹动画
12 |
13 | ```dart
14 | new InkWell(
15 | onTap: (){
16 | final snackBar = new SnackBar(content: new Text('Tap'));
17 | Scaffold.of(content).showSnackBar(snackBar);
18 | },
19 | child: new Container(
20 | padding: const EdgeInsets.all(12.0),
21 | child: new Text('Flat Button'),
22 | )
23 | )
24 | ```
25 |
26 | ## 完整示例
27 |
28 | ```dart
29 | import 'package:flutter/material.dart';
30 |
31 | void main() => runApp(new MyApp());
32 |
33 | class MyApp extends StatelessWidget {
34 | @override
35 | Widget build(BuildContext context) {
36 | final title = 'InkWell Demo';
37 |
38 | return new MaterialApp(
39 | title: title,
40 | home: new MyHomePage(title: title),
41 | );
42 | }
43 | }
44 |
45 | class MyHomePage extends StatelessWidget {
46 | final String title;
47 |
48 | MyHomePage({Key key, this.title}) : super(key: key);
49 |
50 | @override
51 | Widget build(BuildContext context) {
52 | return new Scaffold(
53 | appBar: new AppBar(
54 | title: new Text(title),
55 | ),
56 | body: new Center(child: new MyButton()),
57 | );
58 | }
59 | }
60 |
61 | class MyButton extends StatelessWidget {
62 | @override
63 | Widget build(BuildContext context) {
64 | // InkWell包装我们自定义的FlatButton
65 | return new InkWell(
66 | // 当点击按钮,显示snackBar
67 | onTap: () {
68 | Scaffold.of(context).showSnackBar(new SnackBar(
69 | content: new Text('Tap'),
70 | ));
71 | },
72 | child: new Container(
73 | padding: new EdgeInsets.all(12.0),
74 | child: new Text('Flat Button'),
75 | ),
76 | );
77 | }
78 | }
79 | ```
80 |
81 | 
--------------------------------------------------------------------------------
/docs/handling_gestures/Handling_Taps.md:
--------------------------------------------------------------------------------
1 | # 处理点击事件
2 |
3 | 我们不仅要向用户展示信息,还要让用户与我们的应用程序交互!那么,我们如何应对诸如点击和拖拽等事件?我们要用[`GestureDetector`](https://docs.flutter.io/flutter/widgets/GestureDetector-class.html)Widget!
4 |
5 | 假设我们想做一个自定义按钮,当点击时显示一个`snackBar`。我们怎么处理这个?
6 |
7 | ## 指南
8 |
9 | 1、创建按钮
10 |
11 | 2、将其包装在`GestureDetector`中,并使用`onTap`回调
12 |
13 | ```dart
14 | new GestureDetector(
15 | child: new Container(
16 | padding: new EdgeInsets.all(12.0),
17 | decoration: new BoxDecoration(
18 | color: Theme.of(context).buttonColor,
19 | borderRadius: new BorderRadius.cicular(8.0)
20 | ),
21 | child: new Text('myButton')
22 | ),
23 | // 当child被点击, 显示snackbar
24 | onTap: (){
25 | final snackBar = new SnackBar(content: new Text('Tap'));
26 | Scaffold.of(context).showSnackBar(snackBar);
27 | }
28 | )
29 | ```
30 |
31 | ## 注意
32 |
33 | 1、如果你想在你的按钮上添加Material波纹效果,请查看[添加Material波纹](https://github.com/isNeilLin/flutter-cookbook/blob/master/docs/handling_gestures/Adding_Material_Touch_ripples.md)。
34 |
35 | 2、虽然我们已经创建了一个自定义按钮来演示这些概念,但Flutter已经包含了一些按钮,其中包括:[`RaisedButton`](https://docs.flutter.io/flutter/material/RaisedButton-class.html), [`FlatButton`](https://docs.flutter.io/flutter/material/FlatButton-class.html), 和 [`CupertinoButton`](https://docs.flutter.io/flutter/cupertino/CupertinoButton-class.html)
36 |
37 | ## 完整示例
38 |
39 | ```dart
40 | import 'package:flutter/material.dart';
41 |
42 | void main() => runApp(new MyApp());
43 |
44 | class MyApp extends StatelessWidget {
45 | @override
46 | Widget build(BuildContext context) {
47 | final title = 'Gesture Demo';
48 |
49 | return new MaterialApp(
50 | title: title,
51 | home: new MyHomePage(title: title),
52 | );
53 | }
54 | }
55 |
56 | class MyHomePage extends StatelessWidget {
57 | final String title;
58 |
59 | MyHomePage({Key key, this.title}) : super(key: key);
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | return new Scaffold(
64 | appBar: new AppBar(
65 | title: new Text(title),
66 | ),
67 | body: new Center(child: new MyButton()),
68 | );
69 | }
70 | }
71 |
72 | class MyButton extends StatelessWidget {
73 | @override
74 | Widget build(BuildContext context) {
75 | // Our GestureDetector wraps our button
76 | return new GestureDetector(
77 | // When the child is tapped, show a snackbar
78 | onTap: () {
79 | final snackBar = new SnackBar(content: new Text("Tap"));
80 |
81 | Scaffold.of(context).showSnackBar(snackBar);
82 | },
83 | // Our Custom Button!
84 | child: new Container(
85 | padding: new EdgeInsets.all(12.0),
86 | decoration: new BoxDecoration(
87 | color: Theme.of(context).buttonColor,
88 | borderRadius: new BorderRadius.circular(8.0),
89 | ),
90 | child: new Text('My Button'),
91 | ),
92 | );
93 | }
94 | }
95 | ```
96 |
97 | 
98 |
--------------------------------------------------------------------------------
/docs/handling_gestures/Implement_Swipe_to_Dismiss.md:
--------------------------------------------------------------------------------
1 | # 滑动移除
2 |
3 | 在许多移动应用程序中,“滑动移除”模式是很常见的。例如,如果我们正在编写一个电子邮件应用程序,我们可能希望允许我们的用户在列表中滑动电子邮件信息。当他们这么做的时候,我们会想要把收件箱里的邮件移到垃圾桶里。
4 |
5 | 通过[`Dismissible`](https://docs.flutter.io/flutter/widgets/Dismissible-class.html)Widget,Flutter使这项任务变得简单。
6 |
7 | ## 指南
8 |
9 | 1、创建列表
10 |
11 | 2、使用`Dismissible`包装列表中的每一项
12 |
13 | 3、提供"Leave Behind"指示
14 |
15 | ## 1、创建列表
16 |
17 | 第一步将是创建一个可以滑动的列表。有关如何创建列表的更详细说明,请查看[处理长列表](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/lists/Working_with_long_lists.md)。
18 |
19 | **创建数据源**
20 |
21 | 在我们的示例中,我们需要使用20个示例项。为了保持简单,我们将生成一个String列表。
22 |
23 | ```dart
24 | final items = new List.generate(20,(i)=>"Item ${i+1}");
25 | ```
26 |
27 | **将数据源转换为列表**
28 |
29 | 首先,我们将简单地显示屏幕上列表中的每个项目。用户将无法滑动这些项目,只是暂时!
30 |
31 | ```dart
32 | new ListView.builder(
33 | itemCount: items.length,
34 | itemBuilder: (cotext, index){
35 | return new ListTile(title: new Text('${items[index]}'));
36 | }
37 | )
38 | ```
39 |
40 | ## 2、使用`Dismissible`包装列表中的每一项
41 |
42 | 现在,我们显示了一个项目列表,我们希望我们的用户能够滑动列表中的每一项!
43 |
44 | 在用户删除列表中子项后,我们需要运行一些代码来从列表中删除该项并显示一个Snackbar。在真正的应用程序中,您可能需要执行更复杂的逻辑,例如从Web服务或数据库中删除项。
45 |
46 | 这就是`Dismissible`Widget发挥作用的地方!在我们的示例中,我们将更新`itemBuilder`函数以返回一个`Dismissible`Widget。
47 |
48 | ```dart
49 | new Dismissible(
50 | // 每个可Dismissible的必须包含一个key
51 | key: new Key(item),
52 | // 我们还需要提供一个功能来告诉我们的应用程序
53 | // 在子项被Dismiss后该怎么办。
54 | onDismissed: (direction){
55 | // 从数据源移除子项
56 | items.removeAt(index);
57 | // 显示snackBar,也可以包含撤销操作
58 | Scaffold.of(context).showSnackBar(
59 | new SnackBar(content: new Text("$item dismissed")));
60 | },
61 | child: new ListTile(title: new Text('${items[index]}'))
62 | )
63 | ```
64 |
65 | ## 3、提供"Leave Behind"指示
66 |
67 | 就目前情况而言,我们的应用程序将允许用户从列表中滑动项目,但这可能不会给他们提供一个可视化的指示,当他们这样做时会发生什么。为了提供一个提示,我们将显示一个“Leave Behind”的指示。
68 |
69 | 为此目的,我们将向`Dismissible`提供一个`background`参数。
70 |
71 | ```dart
72 | new Dismissible(
73 | // 显示红色背景
74 | background: new Container(color: Colors.red),
75 | key: new Key(item),
76 | onDismissed: (direction) {
77 | items.removeAt(index);
78 |
79 | Scaffold.of(context).showSnackBar(
80 | new SnackBar(content: new Text("$item dismissed")));
81 | },
82 | child: new ListTile(title: new Text('$item')),
83 | );
84 | ```
85 |
86 | ## 完整示例
87 |
88 | ```dart
89 | import 'package:flutter/foundation.dart';
90 | import 'package:flutter/material.dart';
91 |
92 | void main() {
93 | runApp(new MyApp(
94 | items: new List.generate(20, (i) => "Item ${i + 1}"),
95 | ));
96 | }
97 |
98 | class MyApp extends StatelessWidget {
99 | final List items;
100 |
101 | MyApp({Key key, @required this.items}) : super(key: key);
102 |
103 | @override
104 | Widget build(BuildContext context) {
105 | final title = 'Dismissing Items';
106 |
107 | return new MaterialApp(
108 | title: title,
109 | home: new Scaffold(
110 | appBar: new AppBar(
111 | title: new Text(title),
112 | ),
113 | body: new ListView.builder(
114 | itemCount: items.length,
115 | itemBuilder: (context, index) {
116 | final item = items[index];
117 |
118 | return new Dismissible(
119 | // 每个可Dismissible的必须包含一个key
120 | key: new Key(item),
121 | // 我们还需要提供一个功能来告诉我们的应用程序
122 | // 在子项被Dismiss后该怎么办。
123 | onDismissed: (direction) {
124 | items.removeAt(index);
125 |
126 | Scaffold.of(context).showSnackBar(
127 | new SnackBar(content: new Text("$item dismissed")));
128 | },
129 | // 显示红色背景
130 | background: new Container(color: Colors.red),
131 | child: new ListTile(title: new Text('$item')),
132 | );
133 | },
134 | ),
135 | ),
136 | );
137 | }
138 | }
139 | ```
140 |
141 | 
--------------------------------------------------------------------------------
/docs/images/Display_images_from_the_internet.md:
--------------------------------------------------------------------------------
1 | # 展示网络图片
2 |
3 | 显示图像是大多数移动应用程序的基础。Flutter提供了[`Image`](https://docs.flutter.io/flutter/widgets/Image-class.html)Widget来显示不同类型的图像。
4 |
5 | 为了处理来自URL的图像,请使用[`Image.network`](https://docs.flutter.io/flutter/widgets/Image/Image.network.html)构造函数。
6 |
7 | ```dart
8 | new Image.network(
9 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
10 | );
11 | ```
12 |
13 | ## 动画GIF
14 |
15 | 令人赞叹的是`Image`Widget也支持即时动画gifs!
16 |
17 | ```dart
18 | new Image.network(
19 | 'https://github.com/flutter/plugins/raw/master/packages/video_player/doc/demo_ipod.gif?raw=true',
20 | )
21 | ```
22 |
23 | ## 占位符和缓存
24 |
25 | `Image.network`默认不处理更高级的功能,例如在将图像加载完成之后淡出图片或图片下载完成之后缓存到设备。要完成这些任务,请参阅以下章节:
26 |
27 | - [淡入带有占位符的图像](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/images/Fade_in_images_with_a_placeholder.md)
28 | - [处理缓存的图片](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/images/Working_with_cached_images.md)
29 |
30 | ## 完整示例
31 |
32 | ```dart
33 | import 'package:flutter/material.dart';
34 |
35 | void main() => runApp(new MyApp());
36 |
37 | class MyApp extends StatelessWidget {
38 | @override
39 | Widget build(BuildContext context){
40 | return new MaterialApp(
41 | title: 'Web Images',
42 | home: new Scaffold(
43 | appBar: new AppBar(
44 | title: new Text('Web Images')
45 | ),
46 | body: new Image.network(
47 | 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
48 | )
49 | )
50 | );
51 | }
52 | }
53 | ```
54 |
55 | 
--------------------------------------------------------------------------------
/docs/images/Fade_in_images_with_a_placeholder.md:
--------------------------------------------------------------------------------
1 | # 淡入带有占位符的图像
2 |
3 | 当使用默认`Image`小部件显示图像时,您可能会注意到它们只是在加载时弹出到屏幕上。这可能会给用户带来视觉上的不适。
4 |
5 | 相反,如果你一开始就能显示一个占位符,并且图像在加载时会褪色,这不是很好吗?我们可以使用[`FadeInImage`](https://docs.flutter.io/flutter/widgets/FadeInImage-class.html),正是为了这个目的!
6 |
7 | `FadeInImage`可以处理任何类型的图像:内存中的图像、本地资源或来自网络的图像。
8 |
9 | 在本例中,我们将使用[transparent_image](https://pub.dartlang.org/packages/transparent_image)作为一个简单的透明占位符。您还可以考虑使用本地资源作为占位符,方法是遵循[Assets and Images](https://flutter.io/assets-and-images/)指南。
10 |
11 | ```dart
12 | new FadeInImage.memoryNetwork(
13 | placeholder: KTransparentImage,
14 | image: 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
15 | )
16 | ```
17 |
18 | ## 完整示例
19 |
20 | ```dart
21 | import 'package:flutter/material.dart';
22 | import 'package:transparent_image/transparent_image.dart';
23 |
24 | void main() => runApp(new MyApp());
25 |
26 | class MyApp extends StatelessWidget {
27 | @override
28 | Widget build(BuildContext context){
29 | final title = 'Fade in images';
30 |
31 | return new MaterialApp(
32 | title: title,
33 | home: new Scaffold(
34 | appBar: new AppBar(
35 | title: new Text(title),
36 | ),
37 | body: new Stack(
38 | children: [
39 | new Center(
40 | child: new CircularProgressIndictor(),
41 | ),
42 | new Center(
43 | child: new FadeInImage.memoryNetwork(
44 | placeholder: KTransparentImage,
45 | image: 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
46 | )
47 | ),
48 | ]
49 | )
50 | )
51 | );
52 | }
53 | }
54 | ```
55 |
56 | 
--------------------------------------------------------------------------------
/docs/images/Working_with_cached_images.md:
--------------------------------------------------------------------------------
1 | # 处理缓存的图片
2 |
3 | 在某些情况下,可以方便地缓存从网络下载的图像,以便它们可以离线使用。为此,我们将使用[cached_network_image](https://pub.dartlang.org/packages/cached_network_image)包。
4 |
5 | 除了缓存之外,`cached_network_image`包在加载时还支持占位符和淡入图像!
6 |
7 | ```dart
8 | new CachedNetworkImage(
9 | imageUrl: 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
10 | )
11 | ```
12 |
13 | ## 加入占位符
14 |
15 | `cached_network_image`包允许我们使用任何Widget作为占位符!在本例中,我们将在图像加载时显示一个loading。
16 |
17 | ```dart
18 | new CachedNetworkImage(
19 | placeholder: new CircularProgressIndicator(),
20 | imageUrl: 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
21 | )
22 | ```
23 |
24 | ## 完整示例
25 |
26 | ```dart
27 | import 'package:flutter/material.dart';
28 | import 'package:cached_network_image/cached_network_image.dart';
29 |
30 | void main() {
31 | runApp(new MyApp());
32 | }
33 |
34 | class MyApp extends StatelessWidget {
35 | @override
36 | Widget build(BuildContext context) {
37 | final title = 'Cached Images';
38 |
39 | return new MaterialApp(
40 | title: title,
41 | home: new Scaffold(
42 | appBar: new AppBar(
43 | title: new Text(title),
44 | ),
45 | body: new Center(
46 | child: new CachedNetworkImage(
47 | placeholder: new CircularProgressIndicator(),
48 | imageUrl:
49 | 'https://github.com/flutter/website/blob/master/_includes/code/layout/lakes/images/lake.jpg?raw=true',
50 | ),
51 | ),
52 | ),
53 | );
54 | }
55 | }
56 | ```
--------------------------------------------------------------------------------
/docs/lists/Create_a_basic_list.md:
--------------------------------------------------------------------------------
1 | # 创建基础列表
2 |
3 | 显示数据列表是移动应用的基本模式。Flutter包括[`ListView`](https://docs.flutter.io/flutter/widgets/ListView-class.html)Widget,使得生成列表易如反掌!
4 |
5 | ## 创建ListView
6 |
7 | 使用标准`ListView`构造函数对于仅包含几个项目的列表来说是完美的。我们还将使用内置的`ListTile`Widget为我们的项目提供视觉结构。
8 |
9 | ```dart
10 | new ListView(
11 | children: [
12 | new ListTile(
13 | leading: new Icon(Icons.map),
14 | title: new Text('Map')
15 | ),
16 | new ListTile(
17 | leading: new Icon(Icons.photo_album),
18 | title: new Text('Album')
19 | ),
20 | new ListTile(
21 | leading: new Icon(Icons.phone),
22 | title: new Text('Phone')
23 | ),
24 | ]
25 | )
26 | ```
27 |
28 | ## 完整示例
29 |
30 | ```dart
31 | import 'package:flutter/material.dart';
32 |
33 | void main() => runApp(new MyApp());
34 |
35 | class MyApp extends StatelessWidget {
36 | @override
37 | Widget build(BuildContext context) {
38 | final title = 'Basic List';
39 |
40 | return new MaterialApp(
41 | title: title,
42 | home: new Scaffold(
43 | appBar: new AppBar(
44 | title: new Text(title),
45 | ),
46 | body: new ListView(
47 | children: [
48 | new ListTile(
49 | leading: new Icon(Icons.map),
50 | title: new Text('Map'),
51 | ),
52 | new ListTile(
53 | leading: new Icon(Icons.photo_album),
54 | title: new Text('Album'),
55 | ),
56 | new ListTile(
57 | leading: new Icon(Icons.phone),
58 | title: new Text('Phone'),
59 | ),
60 | ],
61 | ),
62 | ),
63 | );
64 | }
65 | }
66 | ```
67 |
68 | 
--------------------------------------------------------------------------------
/docs/lists/Creating_a_grid_List.md:
--------------------------------------------------------------------------------
1 | # 创建网格列表
2 |
3 | 在某些情况下,您可能希望将子项显示为一个网格,而不是一个一个接一个的正常列表。对于这个需求,我们将使用[`GridView `](https://docs.flutter.io/flutter/widgets/GridView-class.html)Widget。
4 |
5 | 开始使用网格的最简单方法是使用[`GridView.count`](https://docs.flutter.io/flutter/widgets/GridView/GridView.count.html)构造函数,因为它允许我们指定我们想要的行或列数。
6 |
7 | 在本例中,我们将生成一个由100个小部件组成的列表,在列表中显示它们的索引。这将帮助我们可视化的认识`GridView`的工作方式。
8 |
9 | ```dart
10 | new GridView.count(
11 | //创建具有2个列的网格
12 | //如果将scrollDirection更改为horizontal,这将产生2行
13 | crossAxisCount: 2,
14 | //生成在列表中显示索引的100个小部件
15 | children: new List.generate(100,(index){
16 | return new Center(
17 | child: new Text(
18 | 'Item ${index}',
19 | style: Theme.of(context).textTheme.headline
20 | )
21 | );
22 | })
23 | )
24 | ```
25 |
26 | ## 完整示例
27 |
28 | ```dart
29 | import 'package:flutter/material.dart';
30 |
31 | void main() {
32 | runApp(new MyApp());
33 | }
34 |
35 | class MyApp extends StatelessWidget {
36 | @override
37 | Widget build(BuildContext context) {
38 | final title = 'Grid List';
39 |
40 | return new MaterialApp(
41 | title: title,
42 | home: new Scaffold(
43 | appBar: new AppBar(
44 | title: new Text(title),
45 | ),
46 | body: new GridView.count(
47 | //创建具有2个列的网格
48 | //如果将scrollDirection更改为horizontal,这将产生2行
49 | crossAxisCount: 2,
50 | //生成在列表中显示索引的100个小部件
51 | children: new List.generate(100, (index) {
52 | return new Center(
53 | child: new Text(
54 | 'Item $index',
55 | style: Theme.of(context).textTheme.headline,
56 | ),
57 | );
58 | }),
59 | ),
60 | ),
61 | );
62 | }
63 | }
64 | ```
65 |
--------------------------------------------------------------------------------
/docs/lists/Creating_lists_with_different_types_of_items.md:
--------------------------------------------------------------------------------
1 | # 创建具有不同类型项的列表
2 |
3 | 我们通常需要创建显示不同类型内容的列表。例如,我们可能正在处理一个列表,该列表显示一个标题,后面跟着几个与标题相关的项,然后是另一个标题,依此类推。
4 |
5 | 我们怎样才能创造出这样一种Flutter的结构呢?
6 |
7 | ## 指南
8 |
9 | 1、使用不同类型的项创建数据源
10 |
11 | 2、将数据源转换为小部件列表
12 |
13 | ## 1、使用不同类型的项创建数据源
14 |
15 | 为了在列表中表示不同类型的项,我们需要为每种类型的项定义一个类。
16 |
17 | 在本例中,我们将开发一个应用程序,它显示一个标题,后面跟着五个消息。因此,我们将创建三个类:`ListItem`、`HeadingItem`和`MessageItem`。
18 |
19 | ```dart
20 | //可以包含的不同类型项的基类
21 | abstract class ListItem {}
22 |
23 | //包含显示标题的数据的ListItem
24 | class HeadingItem implements ListItem {
25 | final String heading;
26 |
27 | HeadingItem(this.heading);
28 | }
29 |
30 | // 包含显示消息的数据的ListItem
31 | class MessageItem implements ListItem {
32 | final String sender;
33 | final String body;
34 |
35 | MessageItem({this.sender,this.body});
36 | }
37 | ```
38 |
39 | 大多数情况下,我们会从互联网或本地数据库中获取数据,并将这些数据转换为项目列表。
40 |
41 | 对于本例,我们将生成要使用的列表。该列表将包含一个标题,后面是五条消息。然后重复。
42 |
43 | ```dart
44 | final items = new List.generate(
45 | 1200,
46 | (i) => i % 6 == 0
47 | ? HeadingItem("Heading ${i}")
48 | : MessageItem("Sender ${i}", "Message body ${i}")
49 | );
50 | ```
51 |
52 | ## 2、将数据源转换为小部件列表
53 |
54 | 为了将每个项转换为Widget,我们将使用[`ListView.Builder`](https://docs.flutter.io/flutter/widgets/ListView/ListView.builder.html)构造函数。
55 |
56 | 通常,我们希望提供一个`builder`函数来检查我们所处理的项目的类型,并为该类型的项返回适当的Widget。
57 |
58 | 在本例中,使用`is`关键字检查我们正在处理的项的类型是很方便的。它很快,并将自动将每个项目转换为适当的类型。但是,如果您喜欢另一种模式,则有不同的方法来处理这个问题!
59 |
60 | ```dart
61 | new ListView.builder(
62 | itemCoount: items.length,
63 | itemBuilder: (context, index){
64 | final item = items[index];
65 | if(item is HeadingItem){
66 | return new ListTile(
67 | titll: new Text(
68 | item.heading,
69 | style: Theme.of(context).textTheme.headline
70 | )
71 | );
72 | }else if(item is MessageItem){
73 | return new ListTile(
74 | title: new Text(item.sender),
75 | subtitle: new Text(item.body)
76 | );
77 | }
78 | }
79 | )
80 | ```
81 |
82 | ## 完整示例
83 |
84 | ```dart
85 | import 'package:flutter/foundation.dart';
86 | import 'package:flutter/material.dart';
87 |
88 | void main(){
89 | runApp(new MyApp(
90 | items: new List.generate(
91 | 1200,
92 | (i) => i % 6 == 0
93 | ? HeadingItem("Heading ${i}")
94 | : MessageItem("Sender ${i}", "Message body ${i}")
95 | )
96 | }
97 |
98 | class MyApp extends StatelessWidget {
99 |
100 | final List items;
101 |
102 | MyApp({Key: key, @required this.items}) : super(key:key);
103 |
104 | @override
105 | Widget build(BuildContext context){
106 | final title = 'Mixed List';
107 | return new MaterialApp(
108 | title: title,
109 | home: new Scaffold(
110 | appBar: new AppBar(
111 | title: new Text(title),
112 | ),
113 | body: new ListView.builder(
114 | itemCount: items.length,
115 | itemBuilder: (context, index){
116 | final item = items[index];
117 | if(item is HeadingItem){
118 | return new ListTile(
119 | titll: new Text(
120 | item.heading,
121 | style: Theme.of(context).textTheme.headline
122 | )
123 | );
124 | }else if(item is MessageItem){
125 | return new ListTile(
126 | title: new Text(item.sender),
127 | subtitle: new Text(item.body)
128 | );
129 | }
130 | }
131 | )
132 | )
133 | );
134 | }
135 | }
136 |
137 | //可以包含的不同类型项的基类
138 | abstract class ListItem {}
139 |
140 | //包含显示标题的数据的ListItem
141 | class HeadingItem implements ListItem {
142 | final String heading;
143 |
144 | HeadingItem(this.heading);
145 | }
146 |
147 | // 包含显示消息的数据的ListItem
148 | class MessageItem implements ListItem {
149 | final String sender;
150 | final String body;
151 |
152 | MessageItem({this.sender,this.body});
153 | }
154 | ```
155 |
156 | 
--------------------------------------------------------------------------------
/docs/lists/Make_a_horizontal_list.md:
--------------------------------------------------------------------------------
1 | # 创建水平列表
2 |
3 | 有时,您可能希望创建一个水平滚动而不是垂直滚动的列表。[`ListView`](https://docs.flutter.io/flutter/widgets/ListView-class.html)Widget支持水平列表。我们将使用标准的`ListView`构造函数,通过`scrollDirection`属性,这将覆盖默认的垂直方向。
4 |
5 | ```dart
6 | new ListView(
7 | scrollDirection: Axis.horizontal,
8 | children: [
9 | new Container(
10 | width: 160.0,
11 | color: Colors.red,
12 | ),
13 | new Container(
14 | width: 160.0,
15 | color: Colors.blue,
16 | ),
17 | new Container(
18 | width: 160.0,
19 | color: Colors.green,
20 | ),
21 | new Container(
22 | width: 160.0,
23 | color: Colors.yellow,
24 | ),
25 | new Container(
26 | width: 160.0,
27 | color: Colors.orange,
28 | ),
29 | ]
30 | )
31 | ```
32 |
33 | ## 完整示例
34 |
35 | ```dart
36 | import 'package:flutter/material.dart';
37 |
38 | void main() => runApp(new MyApp());
39 |
40 | class MyApp extends StatelessWidget {
41 | @override
42 | Widget build(BuildContext context) {
43 | final title = 'Horizontal List';
44 |
45 | return new MaterialApp(
46 | title: title,
47 | home: new Scaffold(
48 | appBar: new AppBar(
49 | title: new Text(title),
50 | ),
51 | body: new Container(
52 | margin: new EdgeInsets.symmetric(vertical: 20.0),
53 | height: 200.0,
54 | child: new ListView(
55 | scrollDirection: Axis.horizontal,
56 | children: [
57 | new Container(
58 | width: 160.0,
59 | color: Colors.red,
60 | ),
61 | new Container(
62 | width: 160.0,
63 | color: Colors.blue,
64 | ),
65 | new Container(
66 | width: 160.0,
67 | color: Colors.green,
68 | ),
69 | new Container(
70 | width: 160.0,
71 | color: Colors.yellow,
72 | ),
73 | new Container(
74 | width: 160.0,
75 | color: Colors.orange,
76 | ),
77 | ],
78 | ),
79 | ),
80 | ),
81 | );
82 | }
83 | }
84 | ```
85 |
86 | 
--------------------------------------------------------------------------------
/docs/lists/Working_with_long_lists.md:
--------------------------------------------------------------------------------
1 | # 处理长列表
2 |
3 | 标准的[`ListView`](https://docs.flutter.io/flutter/widgets/ListView-class.html)构造函数对于小列表效果很好。但为了处理包含大量子项的列表,最好使用[`ListView.Builder`](https://docs.flutter.io/flutter/widgets/ListView/ListView.builder.html)构造函数。
4 |
5 | 默认的`ListView`构造函数要求我们一次创建所有子项,`ListView.Builder`构造函数将在滚动到屏幕上时创建子项。
6 |
7 | ## 1、创建数据源
8 |
9 | 首先,我们需要一个可以使用的数据源。例如,数据源可能是存储中的消息、搜索结果或产品的列表。大多数情况下,这些数据将来自网络或数据库。
10 |
11 | 对于本例,我们将使用[`List.generate`](https://docs.flutter.io/flutter/dart-core/List/List.generate.html)构造函数生成一个包含10000个字符串的列表。
12 |
13 | ```dart
14 | final items = new List.generate(10000, (i)=>"Item ${i}");
15 | ```
16 |
17 | ## 2、将数据源转化为Widgets
18 |
19 | 为了显示字符串列表,我们需要将每个字符串呈现为Widget!
20 |
21 | 这就是`ListView.builder`发挥作用的地方。在我们的例子中,我们将在每行上显示一个字符串。
22 |
23 | ```dart
24 | new ListView.builder(
25 | itemCount: items.length,
26 | itemBuilder: (context, index){
27 | return new ListTile(
28 | title: new Text('${items[index]}')
29 | )
30 | }
31 | )
32 | ```
33 |
34 | ## 完整示例
35 |
36 | ```dart
37 | import 'package:flutter/foundation.dart';
38 | import 'package:flutter/material.dart';
39 |
40 | void main() {
41 | runApp(new MyApp(
42 | items: new List.generate(10000, (i)=>"Item ${i}")
43 | ));
44 | }
45 |
46 | class MyApp extends StatelessWidget {
47 | final List items;
48 |
49 | MyApp({Key: key, this.items}) : super(key: key);
50 |
51 | @override
52 | Widget build(BuildContext context){
53 | final title = 'Long List';
54 | return new MaterialApp(
55 | title: title,
56 | home: new Scaffold(
57 | appBar: new AppBar(
58 | title: new Text(title)
59 | ),
60 | body: new ListView.builder(
61 | itemCount: items.length,
62 | itemBuilder: (context, index){
63 | return new ListTile(
64 | title: new Text("${items[index]}")
65 | );
66 | }
67 | )
68 | )
69 | );
70 | }
71 | }
72 | ```
73 |
74 | 
--------------------------------------------------------------------------------
/docs/navigation/Animating_a_Widget_across_screens.md:
--------------------------------------------------------------------------------
1 | # 一个屏幕动画到另一个屏幕
2 |
3 | 在用户从一个屏幕导航到另一个屏幕时,引导用户浏览我们的应用程序通常是有帮助的。引导用户通过应用程序的一种常见技术是将Widget从一个屏幕动画到另一个屏幕。这将创建一个连接这两个屏幕的可视锚点。
4 |
5 | 我们如何用Flutter将一个Widget从一个屏幕动画到另一个屏幕?用[`Hero`](https://docs.flutter.io/flutter/widgets/Hero-class.html)部件!
6 |
7 | ## 指南
8 |
9 | 1、创建显示相同图片的两个Screen
10 |
11 | 2、添加`Hero`部件到第一个Screen
12 |
13 | 3、添加`Hero`部件到第二个Screen
14 |
15 | ## 1、创建显示相同图片的两个Screen
16 |
17 | 在本例中,我们将在两个Screen上显示相同的图像。当用户点击图像时,我们希望将图像从第一个屏幕动画到第二个屏幕。现在,我们将创建可视化结构,并在接下来的步骤中处理动画!
18 |
19 | ```dart
20 | class MainScreen extends StatelessWidget {
21 | @override
22 | Widget build(BuildContext context){
23 | return new Scaffold(
24 | appBar: new AppBar(
25 | title: new Text('Main Screen'),
26 | ),
27 | body: new GestureDetector(
28 | onTap: (){
29 | Navigator.push(context,new MaterialPageRoute(
30 | builder: (_) => new DetailScreen()
31 | ));
32 | },
33 | child: new Image.network(
34 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
35 | )
36 | )
37 | );
38 | }
39 | }
40 |
41 | class DetailScreen extends StatelessWidget {
42 | @override
43 | Widget build(BuildContext context){
44 | return new Scaffold(
45 | body: new GestureDetector(
46 | onTap: (){
47 | Navigator.pop(context);
48 | },
49 | child: new Center(
50 | child: new Image.network(
51 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
52 | ),
53 | ),
54 | )
55 | );
56 | }
57 | }
58 | ```
59 |
60 | ## 2、添加`Hero`部件到第一个Screen
61 |
62 | 为了将这两个Screen用动画连接起来,我们需要将`Image`Widget封装在`Her`oWidget中。`Hero`Widget需要两个参数:
63 |
64 | 1、`tag`:识别`Hero`的对象。两个Screen上都是一样的
65 |
66 | 2、 `child`:我们想在Screen上做动画的Widget。
67 |
68 | ```dart
69 | new Hero(
70 | tag: 'imageHero',
71 | child: new Image.network(
72 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
73 | ),
74 | )
75 | ```
76 |
77 | ## 3、添加`Hero`部件到第二个Screen
78 |
79 | 要完成与第一个Screen的连接,我们还需要在第二个Screen上使用`Hero`Widget包装`Image`!它必须使用与第一个Screen相同的`tag`。
80 |
81 | 在将`Hero`Widget应用到第二个Screen后,Screen之间的动画将工作!
82 |
83 | ```dart
84 | new Hero(
85 | tag: 'imageHero',
86 | child: new Image.network(
87 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
88 | ),
89 | )
90 | ```
91 |
92 | > 注意:此代码与我们在第一个屏幕上的代码相同!通常,您可以创建一个可重用的Widget,而不是重复代码,但是对于这个示例,我们将复制代码以进行演示。
93 |
94 | ## 完整示例
95 |
96 | ```dart
97 | import 'package:flutter/material.dart';
98 |
99 | void main() => runApp(new HeroApp());
100 |
101 | class HeroApp extends StatelessWidget {
102 | @override
103 | Widget build(BuildContext context){
104 | return new MaterialApp(
105 | title: 'Transition Demo',
106 | home: new MainScreen()
107 | );
108 | }
109 | }
110 |
111 | class MainScreen extends StatelessWidget {
112 | @override
113 | Widget build(BuildContext context){
114 | return new Scaffold(
115 | appBar: new AppBar(
116 | title: new Text('Main Screen')
117 | ),
118 | body: new GestureDetector(
119 | onTap: (){
120 | Navigator.push(context,new MaterialPageRoute(
121 | builder: (_) => new DetailScreen()
122 | ));
123 | },
124 | child: new Hero(
125 | tag: 'imageHero',
126 | child: new Image.network(
127 | 'https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg',
128 | )
129 | )
130 | )
131 | );
132 | }
133 | }
134 |
135 | class DetailScreen extends StatelessWidget {
136 | @override
137 | Widget build(BuildContext context){
138 | return new Scaffold(
139 | body: new GestureDetector(
140 | onTap: () {
141 | Navigator.pop(context);
142 | },
143 | child: new Center(
144 | child: new Hero(
145 | tag: 'imageHero',
146 | child: new Image.network('https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg')
147 | )
148 | )
149 | )
150 | );
151 | }
152 | }
153 | ```
154 |
155 | 
156 |
--------------------------------------------------------------------------------
/docs/navigation/Navigate_to_a_new_screen_and_back.md:
--------------------------------------------------------------------------------
1 | # 导航到新页面并返回
2 |
3 | 大多数应用程序都包含几个显示不同类型信息的Screen。例如,我们可能有一个显示产品的Screen。我们的用户可以点击一个产品,在一个新的Screen获得更多的信息。
4 |
5 | 从Android的角度来说,我们的Screen将是新的Activities。在IOS术语中,新的ViewController。在Flutter中,Screen只是Widget(部件)。
6 |
7 | 那么,我们如何导航到新的Screen呢?用[`Navigator`](https://docs.flutter.io/flutter/widgets/Navigator-class.html)!
8 |
9 | ## 指南
10 |
11 | 1、创建两个Screen
12 |
13 | 2、导航到第二个Screen使用`Navigator.push`
14 |
15 | 3、回到第一个Screen使用`Navigator.pop`
16 |
17 | ## 1、创建两个Screen
18 |
19 | 首先,我们需要使用两个Screen。因为这是一个基本的例子,我们将创建两个Screen,每个Screen包含一个按钮。点击第一个Screen上的按钮将导航到第二个Screen。点击第二个Screen上的按钮将我们的用户返回到第一个!
20 |
21 | 首先,我们将建立视觉结构。
22 |
23 | ```dart
24 | class FirstScreen extends StatelessWidget {
25 | @override
26 | Widget build(BuildContext context){
27 | return new Scaffold(
28 | appBar: new AppBar(
29 | title: new Text('First Screen'),
30 | ),
31 | body: new Center(
32 | child: new RaisedButton(
33 | child: new Text('Launch new screen'),
34 | onPressed: (){
35 | //导航到第二个Screen
36 | }
37 | )
38 | )
39 | );
40 | }
41 | }
42 |
43 | class SecondScreen extends StatelessWidget {
44 | @override
45 | Widget build(BuildContext context){
46 | return new Scaffold(
47 | appBar: new AppBar(
48 | title: new Text('Second Screen'),
49 | ),
50 | body: new Center(
51 | child: new RaisedButton(
52 | child: new Text('Go Back'),
53 | onPressed: (){
54 | //导航到第一个Screen
55 | }
56 | )
57 | )
58 | );
59 | }
60 | }
61 | ```
62 |
63 | ## 2、导航到第二个Screen使用`Navigator.push`
64 |
65 | 为了导航到一个新的Screen,我们需要使用[`Navigator.push`](https://docs.flutter.io/flutter/widgets/Navigator/push.html)方法,`push`方法将向由Navigator管理的路由堆栈中添加一个`Route`!
66 |
67 | `push`方法需要一个`Route`,但是`Route`从何而来?我们可以创建自己的,也可以直接使用[`MaterialPageRoute`](https://docs.flutter.io/flutter/material/MaterialPageRoute-class.html)。`MaterialPageRoute`非常方便,因为它使用特定于平台的动画转换到新Screen。
68 |
69 | 在`FirstScreen`Widget的`build`方法中,我们将更新`onPressed`回调:
70 |
71 | ```dart
72 | // FirstScreen部件
73 | onPressed: (){
74 | Navigator.push(
75 | context,
76 | new MaterialPageRoute(
77 | build: (context)=>new SecondScreen()
78 | )
79 | )
80 | }
81 | ```
82 |
83 | ## 3、回到第一个Screen使用`Navigator.pop`
84 |
85 | 现在我们已经进入了第二个Screen,我们如何关闭它并返回到第一个Screen呢?使用[`Navigator.pop`](https://docs.flutter.io/flutter/widgets/Navigator/pop.html)方法!`pop`方法将从导航器管理的路由堆栈中删除当前`Route`。
86 |
87 | 对于此部分,我们需要更新在我们的`SecondScreen`部件中的`onPressed`回调:
88 |
89 | ```dart
90 | // SecondScreen部件
91 | onPressed: (){
92 | Navigator.pop(context);
93 | }
94 | ```
95 |
96 | ## 完整示例
97 |
98 | ```dart
99 | import 'package:flutter/material.dart';
100 |
101 | void main() {
102 | runApp(new MaterialApp(
103 | title: 'Navigation Basics',
104 | home: new FirstScreen(),
105 | ));
106 | }
107 |
108 | class FirstScreen extends StatelessWidget {
109 | @override
110 | Widget build(BuildContext context) {
111 | return new Scaffold(
112 | appBar: new AppBar(
113 | title: new Text('First Screen'),
114 | ),
115 | body: new Center(
116 | child: new RaisedButton(
117 | child: new Text('Launch new screen'),
118 | onPressed: () {
119 | Navigator.push(
120 | context,
121 | new MaterialPageRoute(builder: (context) => new SecondScreen()),
122 | );
123 | },
124 | ),
125 | ),
126 | );
127 | }
128 | }
129 |
130 | class SecondScreen extends StatelessWidget {
131 | @override
132 | Widget build(BuildContext context) {
133 | return new Scaffold(
134 | appBar: new AppBar(
135 | title: new Text("Second Screen"),
136 | ),
137 | body: new Center(
138 | child: new RaisedButton(
139 | onPressed: () {
140 | Navigator.pop(context);
141 | },
142 | child: new Text('Go back!'),
143 | ),
144 | ),
145 | );
146 | }
147 | }
148 | ```
149 |
150 | 
--------------------------------------------------------------------------------
/docs/navigation/Navigate_with_named_routes.md:
--------------------------------------------------------------------------------
1 | # 导航到命名路由
2 |
3 | 在[导航到新页面并返回](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/navigation/Navigate_to_a_new_screen_and_back.md)章节中,我们学习了如何通过创建一个新的路由导航到新的页面并将它添加到[`Navigator`](https://docs.flutter.io/flutter/widgets/Navigator-class.html)。
4 |
5 | 但是,如果我们需要在应用程序的许多部分导航到相同的屏幕,这可能会导致代码重复。在这些情况下,定义“命名路由”并使用命名路由进行导航是非常方便的。
6 |
7 | 要使用指定的路由,我们可以使用[`Navigator.pushNamed`](https://docs.flutter.io/flutter/widgets/Navigator/pushNamed.html)函数。此示例将演示如何使用命名路由。
8 |
9 | ## 步骤
10 |
11 | 1、 创建两个页面
12 |
13 | 2、 定义路由
14 |
15 | 3、 使用`Navigator.pushNamed`导航到第二个页面
16 |
17 | 4、 使用`Navigator.pop`返回到第一个页面
18 |
19 | ## 1. 创建两个页面
20 |
21 | 首先,我们需要使用两个页面。第一个页面将包含导航到第二个页面的按钮。第二个页面将包含一个导航回到第一个页面的按钮。
22 |
23 | ```dart
24 | class FirstScreen extends StatelessWidget {
25 | @override
26 | Widget build(BuildContext context) {
27 | return Scaffold(
28 | appBar: AppBar(
29 | title: Text('First Screen'),
30 | ),
31 | body: Center(
32 | child: RaisedButton(
33 | child: Text('Launch screen'),
34 | onPressed: () {
35 | // 点击导航到第二个页面
36 | },
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
43 | class SecondScreen extends StatelessWidget {
44 | @override
45 | Widget build(BuildContext context) {
46 | return Scaffold(
47 | appBar: AppBar(
48 | title: Text("Second Screen"),
49 | ),
50 | body: Center(
51 | child: RaisedButton(
52 | onPressed: () {
53 | // 点击返回到第一个页面
54 | },
55 | child: Text('Go back!'),
56 | ),
57 | ),
58 | );
59 | }
60 | }
61 | ```
62 |
63 | ## 2. 定义路由
64 |
65 | 接下来,我们需要通过向[`MaterialApp`](https://docs.flutter.io/flutter/material/MaterialApp-class.html)构造函数提供额外的属性来定义我们的路由`initialRoute`和`routes`本身。
66 |
67 | `initialRoute`属性定义了我们的应用程序应该从哪个路由开始。`routes`属性定义了可用的命名路由和在导航到这些路由时应该构建的部件。
68 |
69 | ```dart
70 | MaterialApp(
71 | // 从"/"路由这里开始,在本例中是第一个页面
72 | initialRoute: '/',
73 | routes: {
74 | // 当导航到"/"时,构建第一个页面
75 | '/': (context) => FirstScreen(),
76 | // 当导航到"/second"时,构建第二个页面
77 | '/second': (context) => SecondScreen(),
78 | },
79 | );
80 | ```
81 |
82 | > 注意:当使用`initialRoute`时,请确保没有定义`home`属性。
83 |
84 | ## 3. 使用`Navigator.pushNamed`导航到第二个页面
85 |
86 | 在部件和路由完成之后,我们就可以开始导航了!在本例中,我们将使用[`Navigator.pushNamed`](https://docs.flutter.io/flutter/widgets/Navigator/pushNamed.html)函数。这告诉Fluter构建在我们的路由表中定义的Widget并启动屏幕。
87 |
88 | 在`FirstScreen`部件的`build`方法中,我们需要更新`onPressed`回调函数:
89 |
90 | ```dart
91 | // in FirstScreen Widget
92 | onPressed: () {
93 | Navigator.pushNamed(context, '/second');
94 | }
95 | ```
96 |
97 | ## 4. 使用`Navigator.pop`返回到第一个页面
98 |
99 | 为了回到第一个页面,我们可以使用[`Navigator.pop`](https://docs.flutter.io/flutter/widgets/Navigator/pop.html)函数。
100 |
101 | ```dart
102 | // in SecondScreen Widget
103 | onPressed: () {
104 | Navigator.pop(context);
105 | }
106 | ```
107 |
108 | ## 完整示例
109 |
110 | ```dart
111 | import 'package:flutter/material.dart';
112 |
113 | void main() {
114 | runApp(MaterialApp(
115 | title: 'Named Routes Demo',
116 | // 从"/"路由这里开始,在本例中是第一个页面
117 | initialRoute: '/',
118 | routes: {
119 | // 当导航到"/"时,构建第一个页面
120 | '/': (context) => FirstScreen(),
121 | // 当导航到"/second"时,构建第二个页面
122 | '/second': (context) => SecondScreen(),
123 | },
124 | ));
125 | }
126 |
127 | class FirstScreen extends StatelessWidget {
128 | @override
129 | Widget build(BuildContext context) {
130 | return Scaffold(
131 | appBar: AppBar(
132 | title: Text('First Screen'),
133 | ),
134 | body: Center(
135 | child: RaisedButton(
136 | child: Text('Launch screen'),
137 | onPressed: () {
138 | // 使用命名路由导航到第二个页面
139 | Navigator.pushNamed(context, '/second');
140 | },
141 | ),
142 | ),
143 | );
144 | }
145 | }
146 |
147 | class SecondScreen extends StatelessWidget {
148 | @override
149 | Widget build(BuildContext context) {
150 | return Scaffold(
151 | appBar: AppBar(
152 | title: Text("Second Screen"),
153 | ),
154 | body: Center(
155 | child: RaisedButton(
156 | onPressed: () {
157 | // 通过弹出当前路由从堆栈返回到第一个页面
158 | Navigator.pop(context);
159 | },
160 | child: Text('Go back!'),
161 | ),
162 | ),
163 | );
164 | }
165 | }
166 | ```
167 |
168 | 
169 |
170 |
--------------------------------------------------------------------------------
/docs/navigation/Return_data_from_a_screen.md:
--------------------------------------------------------------------------------
1 | # 从页面返回数据
2 |
3 | 在某些情况下,我们可能希望从新Screen返回数据。例如,我们推送一个新Screen,向用户提供两个选项。当用户点击某个选项时,我们将希望将用户的选择通知我们的第一个Screen,这样它就可以对该信息采取行动了!
4 |
5 | 我们如何才能做到这一点?使用[`Navigator.pop`](https://docs.flutter.io/flutter/widgets/Navigator/pop.html)!
6 |
7 | ## 指南
8 |
9 | 1、定义主屏幕
10 |
11 | 2、添加按钮打开SelectionScreen
12 |
13 | 3、显示SelectionScreen的两个按钮
14 |
15 | 4、当点击按钮,关闭SelectionScreen
16 |
17 | 5、在主屏幕显示带有selection的SnackBar
18 |
19 | ## 1、定义主屏幕
20 |
21 | 主屏幕将会显示一个按钮,当点击时会打开SelectionScreen。
22 |
23 | ```dart
24 | class HomeScreen extends StatelessWidget {
25 | @override
26 | Widget build(BuildContext context){
27 | return new Scaffold(
28 | appBar: new AppBar(
29 | title: new Text('Returning Data Demo')
30 | ),
31 | // 我们将在下一步创建SelectionButton部件
32 | body: new Center(child: new SelectionButton())
33 | );
34 | }
35 | }
36 | ```
37 |
38 | ## 2、添加按钮打开SelectionScreen
39 |
40 | 现在我们将要创建SelectionButton,我们的SelectionButton将会:
41 |
42 | 1、 当被点击的时候打开SelectionScreen
43 |
44 | 2、 等待SelectionScreen返回结果
45 |
46 | ```dart
47 | class SelectionButton extends StatelessWidget {
48 | @override
49 | Widget build(BuildCotext context){
50 | return new RaisedButton(
51 | child: new Text("Pick an option, any option!"),
52 | onPressed: _navigateAndDisplaySelection(context),
53 | );
54 | }
55 |
56 | // 启动SelectionScreen并等待Navigator.pop返回结果
57 | void _navigateAndDisplaySelection(BuildContext context) async{
58 | // Navigator.push在我们调用Navigator.pop之后返回结果
59 | final result = await Navigator.push(
60 | context,
61 | // 我们将在下一步创建SelectionScreen
62 | new MaterialPageRoute(
63 | builder: (context) => new SelectionScreen()
64 | )
65 | );
66 |
67 | }
68 | }
69 | ```
70 |
71 | ## 3、显示SelectionScreen的两个按钮
72 |
73 | 现在我们需要构建SelectionScreen。它将包含两个按钮,当用户点击按钮时,它应该关闭选择SelectionScreen并让主屏幕知道点击了哪个按钮!
74 |
75 | 现在,我们将定义UI,并找出如何在下一步中返回数据。
76 |
77 | ```dart
78 | class SelectionScreen extends StatelessWidget {
79 | @override
80 | Widget build(BuildContext context) {
81 | return new Scaffold(
82 | appBar: new AppBar(
83 | title: new Text('Pick an option'),
84 | ),
85 | body: new Center(
86 | child: new Column(
87 | mainAxisAlignment: MainAxisAlignment.center,
88 | children: [
89 | new Padding(
90 | padding: const EdgeInsets.all(8.0),
91 | child: new RaisedButton(
92 | onPressed: () {
93 | },
94 | child: new Text('Yep!'),
95 | ),
96 | ),
97 | new Padding(
98 | padding: const EdgeInsets.all(8.0),
99 | child: new RaisedButton(
100 | onPressed: () {
101 | },
102 | child: new Text('Nope.'),
103 | ),
104 | )
105 | ],
106 | ),
107 | ),
108 | );
109 | }
110 | }
111 | ```
112 |
113 | ## 4、当点击按钮,关闭SelectionScreen
114 |
115 | 现在,我们要更新两个按钮的`onPressed`回调!为了将数据返回到第一个屏幕,我们需要使用`Navigator.pop`方法。
116 |
117 | `Navigator.pop`接受一个可选的第二个参数,称为`result`。如果我们提供一个`result`,它将返回给`Future`!
118 |
119 | **Yep Button**
120 |
121 | ```dart
122 | new RaisedButton(
123 | onPressed: () {
124 | // 返回"Yep!"作为结果
125 | Navigator.pop(context, 'Yep!');
126 | },
127 | child: new Text('Yep!'),
128 | );
129 | ```
130 |
131 | **Nope Button**
132 |
133 | ```dart
134 | new RaisedButton(
135 | onPressed: () {
136 | // 返回"Nope!"作为结果
137 | Navigator.pop(context, 'Nope!');
138 | },
139 | child: new Text('Nope!'),
140 | );
141 | ```
142 |
143 | ## 5、在主屏幕显示带有selection的SnackBar
144 |
145 | 现在我们启动了一个SelectionScreen并等待结果,我们将用返回的信息做一些事情!
146 |
147 | 在这种情况下,我们将展示一个SnackBar显示结果。我们会更新`SelectionButton`的`_navigateAndDisplaySelection`方法。
148 |
149 | ```dart
150 | void _navigateAndDisplaySelection(BuildContext context) async {
151 | final result = await Navigator.push(
152 | context,
153 | new MaterialPageRoute(
154 | builder: (context) => new SelectionScreen()
155 | )
156 | );
157 |
158 | // SelectionScreen返回结果之后,以SnackBar的形式展示
159 | Scaffold.of(context).showSnackBar(
160 | new SnackBar(content: new Text('${result}'))
161 | );
162 | }
163 | ```
164 |
165 | ## 完整示例
166 |
167 | ```dart
168 | import 'package:flutter/material.dart';
169 |
170 | void main() {
171 | runApp(new MaterialApp(
172 | title: 'Returning Data',
173 | home: new HomeScreen(),
174 | ));
175 | }
176 |
177 | class HomeScreen extends StatelessWidget {
178 | @override
179 | Widget build(BuildContext context) {
180 | return new Scaffold(
181 | appBar: new AppBar(
182 | title: new Text('Returning Data Demo'),
183 | ),
184 | body: new Center(child: new SelectionButton()),
185 | );
186 | }
187 | }
188 |
189 | class SelectionButton extends StatelessWidget {
190 | @override
191 | Widget build(BuildContext context) {
192 | return new RaisedButton(
193 | onPressed: () {
194 | _navigateAndDisplaySelection(context);
195 | },
196 | child: new Text('Pick an option, any option!'),
197 | );
198 | }
199 |
200 | // 启动SelectionScreen并等待Navigator.pop返回结果
201 | _navigateAndDisplaySelection(BuildContext context) async {
202 | // Navigator.push在我们调用Navigator.pop之后返回结果
203 | final result = await Navigator.push(
204 | context,
205 | new MaterialPageRoute(builder: (context) => new SelectionScreen()),
206 | );
207 |
208 | // SelectionScreen返回结果之后,以SnackBar的形式展示
209 | Scaffold
210 | .of(context)
211 | .showSnackBar(new SnackBar(content: new Text("$result")));
212 | }
213 | }
214 |
215 | class SelectionScreen extends StatelessWidget {
216 | @override
217 | Widget build(BuildContext context) {
218 | return new Scaffold(
219 | appBar: new AppBar(
220 | title: new Text('Pick an option'),
221 | ),
222 | body: new Center(
223 | child: new Column(
224 | mainAxisAlignment: MainAxisAlignment.center,
225 | children: [
226 | new Padding(
227 | padding: const EdgeInsets.all(8.0),
228 | child: new RaisedButton(
229 | onPressed: () {
230 | // 返回"Yep!"作为结果
231 | Navigator.pop(context, 'Yep!');
232 | },
233 | child: new Text('Yep!'),
234 | ),
235 | ),
236 | new Padding(
237 | padding: const EdgeInsets.all(8.0),
238 | child: new RaisedButton(
239 | onPressed: () {
240 | // 返回"Nope!"作为结果
241 | Navigator.pop(context, 'Nope.');
242 | },
243 | child: new Text('Nope.'),
244 | ),
245 | )
246 | ],
247 | ),
248 | ),
249 | );
250 | }
251 | }
252 | ```
253 |
254 | 
--------------------------------------------------------------------------------
/docs/navigation/Send_data_to_a_new_screen.md:
--------------------------------------------------------------------------------
1 | # 发送数据到新页面
2 |
3 | 通常,我们不仅希望导航到一个新Screen,而且还将一些数据传递给Screen。例如,我们通常希望传递有关我们所使用的信息。
4 |
5 | 记住:Screen只是Widget。在本例中,我们将创建一个Todos列表。当一个todo被点击时,我们将导航到一个新Screen(Widget),该Screen显示有关todo的信息。
6 |
7 | ## 指南
8 |
9 | 1、定义Todo类
10 |
11 | 2、显示Todos列表
12 |
13 | 3、常见可以显示Todo信息的Detail Screen
14 |
15 | 4、导航并且传递数据到Detail Screen
16 |
17 | ## 1、定义Todo类
18 |
19 | 首先,我们需要一种简单的方法来表示Todos。对于这个例子,我们将创建一个类,它包含两个数据:标题和描述。
20 |
21 | ```dart
22 | class Todo {
23 | final String title;
24 | final String description;
25 | Todo(this.title, this.description);
26 | }
27 | ```
28 |
29 | ## 2、显示Todos列表
30 |
31 | 然后,我们要显示一张待办事项清单。在本例中,我们将生成20个todo并使用ListView显示它们。
32 |
33 | **生成Todos列表**
34 |
35 | ```dart
36 | final todos = new List new Todo(
39 | 'Todo ${i}',
40 | 'A description of waht needs to be done for Todo ${i}'
41 | )
42 | );
43 | ```
44 |
45 | **使用ListView显示Todos列表**
46 |
47 | ```dart
48 | new ListView(
49 | itemCount: 20,
50 | itemBuilder: (context, index){
51 | return new ListTile(
52 | title: new Text(todos[index].title)
53 | );
54 | }
55 | )
56 | ```
57 | 到目前为止,很好。我们将会生成20个待办事项并将其显示在ListView中!
58 |
59 | ## 3、常见可以显示Todo信息的Detail Screen
60 |
61 | 现在,我们将创建第二个Screen。Screen标题将包含待办事项的标题,Screen正文将显示描述。
62 |
63 | 因为它是一个普通的`StatelessWidget`,我们只需要创建Screen的用户传递`Todo`!然后,我们将使用给定的Todo构建一个UI。
64 |
65 | ```dart
66 | class DetailScreen extends StatelessWidget {
67 | // 定义一个变量接收Todo
68 | final Todo todo;
69 |
70 | DetailScreen({Key key, @required this.todo}) : super(key: key);
71 |
72 | @override
73 | Widget build(BuildContext context) {
74 | // 使用Todo创建UI
75 | return new Scaffold(
76 | appBar: new AppBar(
77 | title: new Text("${todo.title}"),
78 | ),
79 | body: new Padding(
80 | padding: new EdgeInsets.all(16.0),
81 | child: new Text('${todo.description}'),
82 | ),
83 | );
84 | }
85 | }
86 | ```
87 |
88 | ## 4、导航并且传递数据到Detail Screen
89 |
90 | 在我们的`DetailScreen`就位后,我们已经准备好执行导航任务了!在我们的例子中,当用户点击列表中的Todo时,我们希望导航到`DetailScreen`。当我们这样做的时候,我们也想把Todo传递给`DetailScreen`。
91 |
92 | 为了实现这一点,我们将为`ListTile`Widget编写一个`onTap`回调。在`onTap`回调中,我们将再次使用`Navigator.push`方法。
93 |
94 | ```dart
95 | new ListView.builder(
96 | itemCount: todos.length,
97 | itemBuilder: (context,index){
98 | return new ListTile(
99 | title: new Text(todos[index].title),
100 | //当用户点击ListTile时,导航到DetailScreen。
101 | //注意,我们不仅导航到一个新的DetailScreen,我们还还将当前的Todo传递给它!
102 | onTap: (){
103 | Navigator.push(
104 | context,
105 | new MaterialPageRoute(
106 | builder: (context)=> new DetailScreen(todo: todos[index])
107 | )
108 | )
109 | }
110 | );
111 | }
112 | )
113 | ```
114 |
115 | ## 完整示例
116 |
117 | ```dart
118 | import 'package:flutter/foundation.dart';
119 | import 'package:flutter/material.dart';
120 |
121 | class Todo {
122 | final String title;
123 | final String description;
124 |
125 | Todo(this.title, this.description);
126 | }
127 |
128 | void main() {
129 | runApp(new MaterialApp(
130 | title: 'Passing Data',
131 | home: new TodosScreen(
132 | todos: new List.generate(
133 | 20,
134 | (i) => new Todo(
135 | 'Todo $i',
136 | 'A description of what needs to be done for Todo $i',
137 | ),
138 | ),
139 | ),
140 | ));
141 | }
142 |
143 | class TodosScreen extends StatelessWidget {
144 | final List todos;
145 |
146 | TodosScreen({Key key, @required this.todos}) : super(key: key);
147 |
148 | @override
149 | Widget build(BuildContext context) {
150 | return new Scaffold(
151 | appBar: new AppBar(
152 | title: new Text('Todos'),
153 | ),
154 | body: new ListView.builder(
155 | itemCount: todos.length,
156 | itemBuilder: (context, index) {
157 | return new ListTile(
158 | title: new Text(todos[index].title),
159 | //当用户点击ListTile时,导航到DetailScreen。
160 | //注意,我们不仅导航到一个新的DetailScreen,我们还还将当前的Todo传递给它!
161 | onTap: () {
162 | Navigator.push(
163 | context,
164 | new MaterialPageRoute(
165 | builder: (context) => new DetailScreen(todo: todos[index]),
166 | ),
167 | );
168 | },
169 | );
170 | },
171 | ),
172 | );
173 | }
174 | }
175 |
176 | class DetailScreen extends StatelessWidget {
177 | // 定义一个变量接收Todo
178 | final Todo todo;
179 |
180 | DetailScreen({Key key, @required this.todo}) : super(key: key);
181 |
182 | @override
183 | Widget build(BuildContext context) {
184 | // 使用Todo创建UI
185 | return new Scaffold(
186 | appBar: new AppBar(
187 | title: new Text("${todo.title}"),
188 | ),
189 | body: new Padding(
190 | padding: new EdgeInsets.all(16.0),
191 | child: new Text('${todo.description}'),
192 | ),
193 | );
194 | }
195 | }
196 | ```
197 |
198 | 
--------------------------------------------------------------------------------
/docs/networking/Fetch_data_from_the_internet.md:
--------------------------------------------------------------------------------
1 | # 获取数据
2 |
3 | 从互联网上获取数据对于大多数应用程序来说都是必要的。幸运的是,Dart和Flutter为这类工作提供了工具!
4 |
5 | ## 指南
6 |
7 | 1、使用`http`包创建网络请求
8 |
9 | 2、将响应转换为自定义的Dart对象
10 |
11 | 3、用Flutter获取和显示数据
12 |
13 | ## 1、使用`http`包创建网络请求
14 |
15 | [`http`](https://pub.dartlang.org/packages/http)包提供了从Internet获取数据的最简单方法。
16 |
17 | 在本例中,我们将使用[`http.get`](https://docs.flutter.io/flutter/package-http_http/package-http_http-library.html)方法从[`JSONPlaceholder REST API`](https://jsonplaceholder.typicode.com/)中获取一个示例数据。
18 |
19 | ```dart
20 | Future fetchPost(){
21 | return http.get('https://jsonplaceholder.typicode.com/posts/1');
22 | }
23 | ```
24 | `http.get`方法返回包含`Response`的`Future`
25 |
26 | - [`Future`](https://docs.flutter.io/flutter/dart-async/Future-class.html)是用于处理异步操作的核心Dart类。它用于表示将来某个时候可能出现的值或错误。
27 |
28 | - `http.Response`类包含从成功的http调用中接收的数据。
29 |
30 | ## 2、将响应转换为自定义的Dart对象
31 |
32 | 虽然发起网络请求很容易,但是使用原始的`Future`并不十分方便。为了简化,我们可以将`http.Response`转换成我们自己的Dart对象。
33 |
34 | **创建一个`Post`类**
35 |
36 | 首先,我们需要创建一个`Post`类,它包含来自网络请求的数据。它还将包括一个工厂构造函数,允许我们从json创建一个`Post`。
37 |
38 | 手动转换JSON只是一种选择。有关更多信息,请参见[`JSON and serialization`](https://flutter.io/json/)
39 |
40 | ```dart
41 | class Post {
42 | final int userId;
43 | final int id;
44 | final String title;
45 | final String body;
46 |
47 | Post({this.userId, this.id, this.title, this.body});
48 |
49 | factory Post.fromJson(Map json){
50 | return new Post(
51 | userId: json['userId'],
52 | id: json['id'],
53 | title: json['title'],
54 | body: json['body']
55 | );
56 | }
57 | }
58 | ```
59 |
60 | **将`http.Response`转化为`Post`**
61 |
62 | 现在,我们将更新`fetchPost`函数以返回`Future`。为此,我们需要:
63 |
64 | 1、使用`dart:convert`包将响应体转换为json`Map`
65 |
66 | 2、使用`fromJson`工厂函数将json`Map`转换为`Post`
67 |
68 | ```dart
69 | Future fetchPost() async {
70 | final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
71 | final json = json.decode(response.body);
72 |
73 | return new Post.fromJson(json);
74 | }
75 | ```
76 |
77 | 终于!我们有了一个功能,可以调用从互联网上获取数据!
78 |
79 | ## 3、用Flutter获取和显示数据
80 |
81 | 为了获取数据并显示在屏幕上,我们可以使用[`FutureBuilder`](https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html)Widget!Flutter附带了`FutureBuilder`Widget,使得使用异步数据源变得很容易。
82 |
83 | 我们必须提供两个参数:
84 |
85 | 1、我们要使用的`Future`,在本例中,我们将调用`fetchPost()`方法
86 |
87 | 2、一个`builder`函数,它根据`Future`的状态,告诉Flutter如何渲染:加载、成功或错误。
88 |
89 | ```dart
90 | new FutureBuilder(
91 | future: fetchPost(),
92 | builder: (context, snapshot) {
93 | if (snapshot.hasData){
94 | return new Text(snapshot.data.title);
95 | } else if (snapshot.hasError) {
96 | return new Text("${snapshot.error}");
97 | }
98 | // 默认显示加载器
99 | return new CircularProgressIndicator();
100 | }
101 | )
102 | ```
103 |
104 | ## 完整示例
105 |
106 | ```dart
107 | import 'dart:async';
108 | import 'dart:convert';
109 |
110 | import 'package:flutter/material.dart';
111 | import 'package:http:/http.dart' as http;
112 |
113 | Future fetchPost() async{
114 | final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
115 | final json = json.decode(response.body);
116 |
117 | return new Post.fromJson(json);
118 | }
119 |
120 | class Post {
121 | final int userId;
122 | final int id;
123 | final String title;
124 | final String body;
125 |
126 | Post({this.userId, this.id, this.title, this.body});
127 |
128 | factory Post.fromJson(Map json){
129 | return new Post(
130 | userId: json['userId'],
131 | id: json['id'],
132 | title: json['title'],
133 | body: json['body']
134 | );
135 | }
136 | }
137 |
138 | void main() => runApp(new MyApp());
139 |
140 | class MyApp extends StatelessWidget {
141 | @override
142 | Widget build(BuildContext context){
143 | return new MaterialApp(
144 | title: 'Fetch Data Example',
145 | theme: new ThemeData(
146 | primarySwatch: Colors.blue
147 | ),
148 | home: new Scaffold(
149 | appBar: new AppBar(
150 | title: new Text('Fetch Data Example')
151 | ),
152 | body: new Center(
153 | child: new FutureBuilder(
154 | future: fetchpost(),
155 | builder: (context, snapshot){
156 | if (snapshot.hasData){
157 | return new Text(snapshot.data.title);
158 | } else if (snapshot.hasError) {
159 | return new Text("${snapshot.error}");
160 | }
161 | // 默认显示加载器
162 | return new CircularProgressIndicator();
163 | }
164 | ),
165 | ),
166 | ),
167 | );
168 | }
169 | }
170 | ```
--------------------------------------------------------------------------------
/docs/networking/Making_authenticated_requests.md:
--------------------------------------------------------------------------------
1 | # 授权认证
2 |
3 | 为了从许多Web服务中获取数据,您需要提供授权。有许多方法可以做到这一点,但最常见的可能是使用`Authorization`HTTP header。
4 |
5 | ## 添加授权header
6 |
7 | `http`包提供了一种向请求中添加header的便捷方法。您还可以利用用于`HttpHeaders`的`dart:io`包
8 |
9 | ```dart
10 | Future fetchPost() {
11 | return http.get(
12 | 'https://jsonplaceholder.typicode.com/posts/1',
13 | // 向后端发送授权头
14 | headers: {
15 | HttpHeaders.AUTHORIZATION: "Basic your_api_token_here",
16 | }
17 | );
18 | }
19 | ```
20 |
21 | ## 完整示例
22 |
23 | 此示例构建在[获取数据](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Fetch_data_from_the_internet.md)的基础上。
24 |
25 | ```dart
26 | import 'dart:async';
27 | import 'dart:convert';
28 | import 'dart:io';
29 | import 'package:http/http.dart' as http;
30 |
31 | Future fetchPost() async {
32 | final response = await http.get(
33 | 'https://jsonplaceholder.typicode.com/posts/1',
34 | // 向后端发送授权头
35 | headers: {
36 | HttpHeaders.AUTHORIZATION: "Basic your_api_token_here",
37 | }
38 | );
39 | final json = json.decode(response);
40 | return new Post.fromJson(json);
41 | }
42 |
43 | class Post {
44 | final int userId;
45 | final int id;
46 | final String title;
47 | final String body;
48 |
49 | Post({this.userId, this.id, this.title, this.body});
50 |
51 | factory Post.fromJson(Map json) {
52 | return new Post(
53 | userId: json['userId'],
54 | id: json['id'],
55 | title: json['title'],
56 | body: json['body'],
57 | );
58 | }
59 | }
60 | ```
--------------------------------------------------------------------------------
/docs/networking/Parsing_JSON_in_the_background.md:
--------------------------------------------------------------------------------
1 | # 后台解析JSON
2 |
3 | 默认情况下,Dart应用程序在一个线程上完成所有的工作。在许多情况下,这个模型简化了编码,而且速度足够快,不会导致应用程序性能差或动画卡顿,通常被称为“jank”。
4 |
5 | 但是,我们可能需要执行大量的计算,例如解析一个非常大的JSON文档。如果这项工作需要超过16毫秒,我们的用户将体验“jank”。
6 |
7 | 为了避免“jank”,我们需要在后台执行这样昂贵的计算。在Android上,这意味着在另一个线程上调度工作。在Flutter中,我们可以使用隔离的[Isolate](https://docs.flutter.io/flutter/dart-isolate/Isolate-class.html)。
8 |
9 | ## 步骤
10 |
11 | 1、 添加`http`包
12 |
13 | 2、 使用http包发出网络请求
14 |
15 | 3、 将响应转换为照片列表
16 |
17 | 4、 将此工作移动到隔离的Isolate
18 |
19 | ## 1. 添加`http`包
20 |
21 | 首先,我们要将[http](https://pub.dartlang.org/packages/http)包添加到我们的项目中。HTTP包可以更容易地执行网络请求,例如从JSON端点获取数据。
22 |
23 | ```
24 | dependencies:
25 | http:
26 | ```
27 |
28 | ## 2. 使用http包发出网络请求
29 |
30 | 在本例中,我们将使用[`http.get()`](https://pub.dartlang.org/documentation/http/latest/http/get.html)方法从[JSONPlaceHolder REST API](https://jsonplaceholder.typicode.com/)获取包含5000个照片对象列表的JSON大型文档。
31 |
32 | ```dart
33 | Future fetchPhotos(http.Client client) async {
34 | return client.get('https://jsonplaceholder.typicode.com/photos');
35 | }
36 | ```
37 |
38 | > 注意:我们为本例中的函数提供了`http.Client`。这将使该功能更容易在不同的环境中测试和使用!
39 |
40 | ## 3. 将响应转换为照片列表
41 |
42 | 接下来,按照[获取数据](https://github.com/isNeilLin/flutter-cookbook/tree/master/docs/networking/Fetch_data_from_the_internet.md)的指导,我们希望将`http.Response`转换为Dart对象列表。这将使数据在将来更容易处理。
43 |
44 | ### 创建一个Photo类
45 |
46 | 首先,我们需要创建一个包含照片数据的`Photo`类。我们还将包括一个`from Json`工厂函数,以便于创建一个以json对象开始的Photo。
47 |
48 | ```dart
49 | class Photo {
50 | final int id;
51 | final String title;
52 | final String thumbnailUrl;
53 |
54 | Photo({this.id, this.title, this.thumbnailUrl});
55 |
56 | factory Photo.fromJson(Map json) {
57 | return Photo(
58 | id: json['id'] as int,
59 | title: json['title'] as String,
60 | thumbnailUrl: json['thumbnailUrl'] as String
61 | );
62 | }
63 | }
64 | ```
65 |
66 | ### 将响应转换为照片列表
67 |
68 | 现在,我们将更新`fetchPhotos`函数,以便它能够返回`Future>`。为此,我们需要:
69 | 1. 创建将响应体转换为`List`的`parsePhotos`方法
70 | 2. 在`fetchPhotos`方法中使用`parsePhotos`方法
71 |
72 | ```dart
73 | List parsePhotos(String responseBody) {
74 | final parsed = json.decode(responseBody).cast