一. 创建Flutter项目
创建Flutter项目有两种方式:
通过命令行创建和通过开发工具创建
1.1. 通过命令行创建
Flutter的名称不要包含特殊的字符,另外不可以使用驼峰标识
flutter create learn_flutter
1.2. 通过开发工具创建
1.3. 默认程序分析
默认项目分析:
目录下有一个
lib文件夹,会存放我们编写的Flutter代码;main.dart,它是我们Flutter启动的入口文件,里面有main函数;二. 开始Flutter代码
2.1. Hello World
import 'package:flutter/material.dart';main(List<String> args) {runApp(Text("Hello World", textDirection: TextDirection.ltr));}
2.2. 代码分析
2.2.1. runApp和Widget
runApp是Flutter内部提供的一个函数,当我们启动一个Flutter应用程序时就是从调用这个函数开始的。
该函数让我们传入一个东西:Widget?
Widget的翻译:小组件或者小控件
Widget到底是什么呢?
- Flutter中万物皆Widget;
- 在iOS或者Android开发中,界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等;
- Flutter中,这些东西都是不同的Widget而已;
- 整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,也需要使用Padding-Widget来做;
runApp函数让我们传入的就是一个Widget:
导入Flutter默认已经给我们提供的Material库,来使用其中的很多内置Widget;
2.2.2. Material设计风格
material是什么呢?
material是Google公司推行的一套
设计风格,或者叫设计语言、设计规范等;- 设计了
颜色、文字的排版、响应动画与过度、填充等等规范; - 在Flutter中高度集成了
Material风格的Widget;
Text小部件分析:
- Text小部件来完成文字的显示;
- Text小部件继承自StatelessWidget,StatelessWidget继承自Widget;
- 所以可以将Text小部件传入到runApp函数中;
属性非常多,只有this.data属性是必须传入的;
class Text extends StatelessWidget {const Text(this.data,{Key key,this.style,this.strutStyle,this.textAlign,this.textDirection,this.locale,this.softWrap,this.overflow,this.textScaleFactor,this.maxLines,this.semanticsLabel,this.textWidthBasis,});}
StatelessWidget简单介绍:
-
2.3. 代码改进
2.3.1. 改进界面样式
居中显示: 需要使用另外一个Widget,
Center;- 文字大一些: 需要给Text文本设置一些样式;
- 我们在Text小部件外层包装了一个Center部件,让Text作为其child;
Text组件设置了一个属性:style,对应的值是TextStyle类型;
import 'package:flutter/material.dart';main(List<String> args) {runApp(Center(child: Text("Hello World",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 36),),));}
2.3.2. 改进界面结构
App页面应该有一定的结构,比如通常都会有
导航栏,会有一些背景颜色等同理可以使用Material库,直接使用其中的一些封装好的组件来完成一些结构的搭建
import 'package:flutter/material.dart';main(List<String> args) {runApp(MaterialApp(home: Scaffold(appBar: AppBar(title: Text("wulei"),),body: Center(child: Text("Hello World",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 36),),),),));}
在最外层包裹一个MaterialApp
意味整个应用都会采用MaterialApp风格的一些东西,方便我们对应用的设计。
- title:这个是定义在Android系统中打开多任务切换窗口时显示的标题;
- home:是该应用启动时显示的页面,我们传入了一个Scaffold;
Scaffold是什么呢?
- 脚手架的作用就是搭建页面的基本结构;
- MaterialApp的home属性传入了一个Scaffold对象,作为启动显示的Widget;
- Scaffold也有一些属性,比如
appBar和body; - appBar是用于设计导航栏的;
body是页面的内容部分,我们传入了之前已经创建好的Center中包裹的一个Text的Widget;
2.3.3. 进阶案例实现
import 'package:flutter/material.dart';main(List<String> args) {runApp(MaterialApp(home: Scaffold(appBar: AppBar(title: Text("wulei"),),body: Center(child: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Checkbox(value: true,onChanged: (value) => print("Hello World")),Text("同意协议",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 20),)],),),),));}
2.4. 代码重构
2.4.1. 创建自己的Widget
Flutter整个开发过程中就是形成一个Widget树,形成嵌套是很正常的;
- Flutter的代码缩进,2/4;
- 可以封装自己的Widget;
- Flutter开发中,可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
- StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已;
StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget;
2.4.2. StatelessWidget
StatelessWidget通常是一些没有状态需要维护的Widget:
数据通常是直接写死(放在Widget中的数据,必须被定义为final);
- 从parent widget中传入的而且一旦传入就不可以修改;
- 从InheritedWidget获取来使用的数据;
创建一个StatelessWidget的格式:
- 继承自StatelessWidget;
StatelessWidget包含一个必须重写的方法:build方法;
class MyStatelessWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;}}
build方法的解析:
Flutter在拿到创建的StatelessWidget时,就会执行它的build方法;
- 需要在build方法中告诉Flutter,自定义的Widget希望渲染什么元素,比如一个Text Widget;
- StatelessWidget没办法主动去执行build方法,当使用的数据发生改变时,build方法会被重新执行;
build方法什么情况下被执行呢?:
- 当StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时);
- 当我们的父Widget(parent widget)发生改变时,子Widget会被重新构建;
如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时;
2.4.3. 重构案例代码
import 'package:flutter/material.dart';main(List<String> args) {runApp(MyApp());}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("wulei"),),body: HomeContent(),),)}}class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Checkbox(value: true,onChanged: (value) => print("Hello World")),Text("同意协议",textDirection: TextDirection.ltr,style: TextStyle(fontSize: 20),)],),);}}
三. 案例练习
3.1. 自定义Widget
产品的展示就是一个大的Widget,这个Widget包含如下Widget:
标题的Widget:使用一个Text Widget完成;
- 描述的Widget:使用一个Text Widget完成;
- 图片的Widget:使用一个Image Widget完成;
- 上面三个Widget要垂直排列,可以使用一个Column的Widget;
三个展示的标题、描述、图片都是不一样的,所以我们可以让Parent Widget来决定内容:
创建三个成员变量保存父Widget传入的数据
class ProductItem extends StatelessWidget {final String title;final String desc;final String imageURL;ProductItem(this.title, this.desc, this.imageURL);@overrideWidget build(BuildContext context) {return Column(children: <Widget>[Text(title, style: TextStyle(fontSize: 24)),Text(desc, style: TextStyle(fontSize: 18)),Image.network(imageURL)],);}}
3.3. 列表数据展示
可以创建三个ProductItem来展示了:
这里采取上面的代码结构进行修改
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(theme: ThemeData(primaryColor: Colors.blueAccent),home: Scaffold(appBar: AppBar(title: Text("wulei"),),body: HomeContent(),),);}}class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Column(children: <Widget>[ProductItem("Apple1", "Macbook Product1", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg"),ProductItem("Apple2", "Macbook Product2", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imm9u5zj30u00k0adf.jpg"),ProductItem("Apple3", "Macbook Product3", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imqlouhj30u00k00v0.jpg"),],);}}
页面超出了可视区域出现报错
如何解决?
- 将Column换成ListView即可;
-
3.4. 案例细节调整
3.4.1. 界面整体边距
希望整个内容距离屏幕的边缘有一定的间距,怎么做呢?
我们需要使用另外一个Widget:Padding,它有一个padding属性用于设置边距大小;
-
3.4.2. 内边距和边框
添加一个内边距,并且还有边框,怎么做呢?
我们可以使用一个Container的Widget,它里面有padding属性,并且可以通过decoration来设置边框;
3.4.3. 文字图片的间距
我们希望给图片和文字之间添加一些间距,怎么做呢?
方式一:给图片或者文字添加一个向上的内边距或者向下的内边距;
方式二:使用SizedBox的Widget,设置一个height属性,可以增加一些距离;
3.5. 最终实现代码
import 'package:flutter/material.dart';main(List<String> args) {runApp(MyApp());}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(theme: ThemeData(primaryColor: Colors.blueAccent),home: Scaffold(appBar: AppBar(title: Text("wulei"),),body: HomeContent(),),);}}class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Padding(padding: const EdgeInsets.all(8.0),child: ListView(children: <Widget>[ProductItem("Apple1", "Macbook Product1", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg"),ProductItem("Apple2", "Macbook Product2", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imm9u5zj30u00k0adf.jpg"),ProductItem("Apple3", "Macbook Product3", "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imqlouhj30u00k00v0.jpg"),],),);}}class ProductItem extends StatelessWidget {final String title;final String desc;final String imageURL;ProductItem(this.title, this.desc, this.imageURL);@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.all(20),decoration: BoxDecoration(border: Border.all()),child: Column(children: <Widget>[Text(title, style: TextStyle(fontSize: 24)),Text(desc, style: TextStyle(fontSize: 18)),SizedBox(height: 10,),Image.network(imageURL)],),);}}
