木辛老师来了,本节课咱们一起把Flutter开发环境搭建起来吧!
请点击右上角“关注”按钮关注我们哟:跟着木辛老师学习Flutter基础编程知识,变身快乐的编程达人吧~
大家好!木辛老师又来了!
经历了自己从零开始创建Flutter项目的激动人心的过程,好多同学已经按耐不住自己激动的心,想要继续探索Flutter这个神秘的世界啦!
今天我们要讲的是Flutter的widget。
在开始详细介绍Widget之前,我们还需要先说一下一个概念:组件化。
组件化是一个被提出好久的设计理念啦,有很多很成功的项目都或多或少地采用了组件化的开发模式。
其实编程和搭建乐高差不多,都是一块积木一块积木地搭建起来的,在这期间,我们也都是将整个搭建过程分成若干部分,单独将各个部分搭建完毕之后,在统一组装,这样很轻松就可以完成一部很复杂的造型。
那么,在过程中分出的若干部分,可以认为就是整个搭建项目中的组件,这也是一个组件化的过程。
说道组件化,好像早在1968年就有一些人提出来了(好早吖)。
虽然很早就有了这个理论,但是直到后来web框架大行其道,这种理论才崭露头角,特别是诸如Angular、React、Vue等技术或者框架流行之后,才慢慢地被更多的技术人员所接受,随后,有越来越多的技术人员开始钻研和使用起来。
其实,组件化这个概念是一个过程:先将复杂的、巨大的项目分解成简单的部分,也就是分解的过程;而后被分解出来的这些简单的部分,再一一被实现之后,再将他们重新组合在一起,也就是合成过程。通过分解和合成最终完成了组件化。
在Flutter的世界,组件都被称为Widget。
也就是平常所说的:万物皆组件,万物皆Widgets。
好在强大的Google已经为我们准备好了一大堆的好用的Widget,为了让大家更方便的时候,也已经随着Flutter的安装,他们“偷偷”地入驻到我们的开发环境中啦。
为了做出更好的应用,不仅可以合理地利用这些现成的widget,我们也可以将这些widget组合在一起“攒”成更适合项目使用的自定义的widget。
通过不同widget的组合,以及组合的组合,就可以实现越来越复杂和强大的功能,这么看来,写程序就越来越像搭建乐高积木啦!
所以,还是那句话,万物皆组件,万物皆Widgets!
“知“”行“合一
做过APP开发的同学们都知道(之前没做过,这是第一次?没关系,请听木辛老师接下来的忽悠ヾ(?°?°?)??),不论是哪个平台的程序开发,都需要分成两个部分来做:
- 软件行为(Behavior):通俗的点讲就是软件它能做什么。所有涉及的业务逻辑都会放在这里一块,比如:数据的读、写、处理等等;
- 软件外表(Presentation):也就是所说的软件的长相,专业点叫做用户界面。比如:按钮、输入框、提示框什么的。
但是!
FLutter把这两部分合二为一啦!
也就是说,你只需要使用Dart一门语言,就可以同时完成业务逻辑的编写和用户界面。这种方式不知道你能不能接受,说实话,这有利也有弊,就看你怎么理解咯。
很多开发框架已经证明:组件化这条路大家一定要一直走下去。虽然长,但是前途很光明!Flutter团队也很适时地公开表示,自己也深受组件化的”毒害“,特别是React,被其很多理念深深印象着。
事实上,不止是Flutter,其他绝大多数的框架开发者都会从其他的优秀框架中借鉴很多概念和思路。相互学习,共同进步嘛,这也是技术飞速发展的一个推动因素吧。
但是!
Flutter在实现用户界面这一块确实独一无二的。这里并没有采纳其他框架的思路,而是采用了相同的Dart语言来实现软件的用户界面和业务逻辑。
要不然,咱们来列个表看看几个比较大的框架的吧:
框架名称 | 业务逻辑实现 | 用户界面实现 |
Xamarin | C# | XAML |
React Native | JavaScript | JSX |
NativeScript | JavaScript | XML |
Flutter | Dart | Dart |
看吧,这样是不是很直观呢!
接下来我们看看如何实现一个Flutter的用户界面吧。
和其他很多框架或者语言一样,一个标准的flutter app也是从面函数开始的。
在Flutter中,main函数会调用一个叫做runApp()的函数。这个runApp()会接收一个Widget,这是最主要的widget,至于它的名字,可以根据你自己的喜好命名,至于符合命名规则即可。
但是,他需要满足一个条件,就是这个类必须要是StatelessWiget的子类:
// 导入Flutter App中需要的dart包
import 'package:flutter/material.dart';
// 这里是入口,需要使用main()函数去调用runApp()函数
void main() => runApp(RootWidget());
// 保证这个类的名字和runApp中调用的名字一致即可
class RootWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text("Hello Flutter", textDirection: TextDirection.ltr);
}
}
大家可以试试在Flutter项目中修改main.dart文件,将上述代码敲进去执行看看效果。
应该很快,就可以看到输出在模拟器左上角的一行”Hello Flutter“字样了吧。咱们终于看到了完全”自主“一段程序,棒棒哒。
先别急着高兴呢!你在敲入这些代码的过程中,有没有发现一些很陌生的东西?
比如:Text()这个函数,这是什么鬼?
这个呢,就是我们之前说的widget了,不过包括text在内的这些都是Flutter已经内置好的widget。既然Flutter已经为咱们准备好了内置widget,咱们就顺便多看看呗,到用的时候咱就可以信手拈来啦。
内置Widgets
Flutter的这些基础的Widgets是创建项目的基础,这一类的组件大约有多少个呢?160多个,足够咱们开发基本的程序使用啦。
所以嘛,好好了解他们,熟练掌握他们的用处,你就可以游刃有余地将他们应用于自己的项目里啦,这可以更加合理的控制,让他们发挥自己最大的”光芒“。
按照他们的主要用途,可以粗略分为这么几个类型:
- Value widgets
- Layout widgets
- Navigation widgets
- Other widgets
当然了,如果你想看到官方更加详细的分类,可以进去如下这个网址:
这里说的很是详细,你们自己细细品吧。
接下来,通过几个小例子,咱们简单的看下几个主要widget吧。熟悉一下,方便你们以后更加深入的学习。
Value widget
这是一类”值控”widget,通过本地存储、网络上的接口服务或者其他地方,这类widget可以获取到一些值,并通过显示或者读取的方式与用户“打成一片”。
这里边最常用的是文本Widget,它就是用来显示文本的。其他的还有显示图片的组件:Image widget,可以显示诸如.jpg、.png等等格式的图片文件。
要不这样吧,我们列一个表格,更直观地看看这些“值控”widget吧:
Checkbox | FormField | RefreshIndicator |
CircularProgressIndicator | Icon | RichText |
Date&Time Pickers | IconButton | Slider |
DataTable | Image | Switch |
DropdownButton | LinearProgressIndicator | Text |
FlatButton | PopupMenuButton | TextField |
FloatingActionButton | Radio | Tooltip |
FlutterLogo | RaisedButton | |
Form | RawImage |
LayoutWidgets
Layout Widgets使得我们可以更加轻松的控制布局,从而让我们的视觉体验更加出色。同步布局widgets,可以实现很多丰富的显示效果,比如:可以将小部件并排放置、上下放置或者也可以实现滚动效果,通过边距设置,还能更好的利用周围控件,避免widgets拥挤在一起,诸如此类的效果。
来看一下这些widgets:
Align | FittedBox | Padding |
AppBar | Flow | PageView |
aspectratio | FractionallysizedBox | placeholder |
Baseline | gridview | row |
Bottomsheet | indexedstack | scaffold |
ButtonBar | intrinsicheight | scrollable |
Card | intrinsicWidth | scrollbar |
Center | LayoutBuilder | singleChildscrollview |
Column | LimitedBox | sizedBox |
ConstrainedBox | ListBody | sizedOverflowBox |
Container | Listtile | sliverappBar |
CustomMultiChildLayout | Listview | snackBar |
divider | MediaQuery | expansionpanel |
stack | expanded | nestedscrollview |
table | OverflowBox | Wrap |
嘿嘿,列了这么多,就是想你看看都有哪些布局widgets而已,你不需要记住。随着我们学习的深入,会慢慢地接触到这些widgets,通过不断的练习,肯定能掌握的。
Navigation Widgets
到目前为止,我们的程序只有一个场景(你可以叫它页面)。但是,但凡是比较“正经”的项目都会有多个页面,而且多个页面之间还不停地跳来跳去,是吧。
所以,遇到这种情况,咱们应该怎么办捏?
这就要用到咱们这部分要讲的内容了:Navigation Widgets。
通过这类的widgets,就可以很简单的实现页面之间的跳转。
通常情况下,我们在APP里可以通过点击按钮,或者有时候点击标签页上的导航按钮,亦或者是从屏幕左侧滑入的抽屉中导航按钮,实现了页面之间的“跳来跳去”。
这些都是比较常用的方式,用户也比较能够接受。来看看Navigation Widgets都有哪些:
AlertDialog | MaterialApp | tabBar |
BottomnavigationBar | Navigator | TabBarView |
drawer | SimpleDialog |
又列出来很多,不过不要着急,后续章节里我们会慢慢讲解哈。
其他的widgets
当然了,除了以上提到过的那些widgets之外,还有很多其他用途的。这一些玩意儿不好说具体属于哪一类的,所以,干脆就叫他们杂类吧,比如:
GestureDetector | Cupertino | Transitions |
Dismissible | Theme | Transforms |
GestureDetector这个widget可能在后续内容里占据了很大的篇幅,因为它貌似是很重要,也是用的比较多的一个。其他的嘛也可能稍微提提,大家了解就可以啦。
创建自己的Stateless widget
接下来,咱们要做的事情就是:不断的将这些内置的widget变着法的“攒”在一起,组成一些适合自己项目的“大部件”,通过这种不断地组合,不断的变幻,从而形成一个我们需要的应用程序。
每个小部件都是一个类,有自己的属性和方法。同时,都有一个构造函数,这个构造函数有零个或者多个参数。最为重要的是,每个widget都有一个build方法,通过这个方法可以接收BuildContext,并返回一个Flutter widget。现在,让我们去他的build方法里看看吧:
class RootWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text("Hello Flutter", textDirection: TextDirection.ltr);
}
}
在这个例子中,我们显示了一个Text Widget。一个单独的内部widget是完全可以工作的,但是,在真实环境下的APP一般会需要一个比较完整、复杂的描述的,是通过很多widget的组合实现的:
class FancyHelloWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('一个稍微复杂点的APP'),
),
body: Container(
alignment: Alignment.center,
child: Text("你好,Flutter"),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.thumb_up),
onPressed: () => {},
),
),
);
}
}
执行程序之后,大家看到的效果应该是这样滴
我们稍微读读源码。
在这个程序中,build这个方法返回了一个MaterialApp 的 widget。你可以认为他是一个容器,通过它返回了Scaffold widget,一并包括其中的三个widgets:一个AppBar、一个Container、一个FloatingActionButton。
通过上边这个结构图,是不是可以更清晰地看出这段程序中widget的结构了呢?Flutter界面程序的编写也就这样,一个widget套入另外多个widget,通过合适的布局就能做出非常美观的APP界面啦。
给widget传递值
大家来说说下边这个公式是什么意思?
y = f(x)
嘿嘿,数学专业的同学们会认为这个是“Y是X的函数”。他很简洁地表达出:随着X(自变量)变化,Y(因变量)将以可预测的方式变化。Flutter也秉承了这一理念。
但是,在Flutter中,公式可以是这样滴:
Scene = f(data)
换句话说,随着应用程序中数据的变化,屏幕上也会发生相应的更改。当你在widget中编写构建方法时,作为一个可以控制程序的“神”,你可以决定如何显示这些数据,这也是Flutter赋予作为开发者的你的一项基本技能。
那么,数据将会如何变化呢?有这么两种方法:
- 外部传入新的数据,从而促使widget进行重新渲染;
- 数据可以在某些widget中“游荡”。
让我们先来说说第一种情况:为widget传入数据。你可以通过构造函数的参数传入数据,就像这样:
Widget build(BuildContext context) {
return Person(“木辛"); // 给widget传入数据:木辛
}
如果一个widget需要表现出一个人的模型,最基本的事情就是需要提供姓名这个数据,就像之前传入的“木辛”那样。
如果你确实需要这样实现,就需要给这个widget添加一个构造函数,以便于能够接受这个传入的值:
class Person extends StatelessWidget {
final String firstName;
Person(this.firstName) {}
Widget build(BuildContext context) {
return Text('$firstName');
}
}
以上是使用Dart语言的语法完成的。其中,需要注意三件事:
- 需要在构造函数中列出需要传入的参数;
- 需要是用“this.”开头,并连接到这参数(这样,就可以明确,这个参数是输入类级别的,不会被混淆到其他范围里去啦);
- 需要将相应的类属性声明为final。
咱们再进一步,刚才只是传入了一个参数,如果需要两个参数应该怎么实现呢?
Widget build(BuildContext context) {
return Person("木辛","庞");
}
只要在对应的位置修改成接受两个参数即可,同时也一定要记得将对应的变量声明为final 类型哟:
class Person extends StatelessWidget {
final String firstName;
final String lastName;
Person(this.firstName, this.lastName) {}
Widget build(BuildContext context) {
return Text('$firstName $lastName');
}
}
其实,还有一种更“友善”的方式:
Widget build(BuildContext context) {
return Person(firstName:"木辛", lastName:"庞");
}
class Person extends StatelessWidget {
final String firstName;
final String lastName;
Person({this.firstName, this.lastName}) {}
Widget build(BuildContext context) {
return Container(child: Text('$firstName $lastName'));
}
}
我特别喜欢这种方式,用的时候特别清晰,不论是自己用,还是给其他人用,都是很不错的实现方式。
对应的类里边,你只需要做一个很细微的修改,即:在构造函数的参数周围添加括号。
木辛老师说
在这里有个更好的编程实践,就是将Person的代码单独放到一个文件里,比如叫做:Person.dart。这样你的项目代码更清晰。使用的时候,只需要使用如下指令即可:
import 'Person.dart';
Stateless and Stateful widgets
到目前为止,我们都是在创建无状态的widget(StatelessWidget)。所以,你可能会猜测应该还有一个有状态的才对!是的,你是对的!
无状态的widget是没有办法保持在机身状态的,而有状态(Stateful widget)的是可以哒。
“状态”呢,值得是widget内部的数据,这些数据在其声明周期内是会变化的。
考虑下之前的Person,如果仅仅是用来显示该人的信息,那么,使用无状态的widget是没有问题的。
但是,如果这是一个维护人员信息的widget呢?并且我们还允许用户通过TextField输入数据呢?是不是就需要一个StatefulWidget了呢?
那到底如何选择,使用StatelessWidget还是StatefulWidget呢?
如无必要,请使用StatelessWidget!切记,切记!
好了,说了这么多,你是不是又对Flutter有了一定层次的了解了呢?在接下来的课程里,我们会继续深入的学习Flutter的其他知识,希望大家喜欢哟!
请大家关注木辛老师的课程哟,获取更多编程知识和编程技巧。接下来,木辛老师和大家一步一步的学习Flutter知识吧。
快乐编程,快乐成长!
咱们下节课再见,88~
本文暂时没有评论,来添加一个吧(●'◡'●)