十二:正式Flutter项目创建
作者 | 主题 |
---|---|
lishengbing | 正式Flutter项目创建 |
一: 创建Flutter项目
创建Flutter项目有两种方式:通过命令行 和 通过开发工具创建
1.1: 通过命令行创建
在终端输入命令:flutter create helloworld 即可
- flutter的名称不要包含特殊的字符、另外不可以使用驼峰标识。
- 创建完之后使用自己喜欢的开发工具打开即可。
2: 通过开发工具创建
我这里也可以使用Android Studio来进行创建
选择Star a new Flutter project
1.3: 默认程序分析
默认程序分析:
- 我们之前已经分析过目录结构了、在目录下有一个lib文件夹、里面会存放我们编写的Flutter代码。
- 打开发现里面有一个main.dart、它是我们Flutter启动的入口文件、里面有main函数;
默认代码分析:
- 这是一个计数器的案例程序、点击右下角+符号、上面显示的数字会递增;
- 但是我们第一次接触main.dart中的代码、可能会发现很多不认识的代码、作为初学者、建议将其中所有的代码都删除掉、从零去创建里面的代码、这样我们才能对Flutter应用程序的结果非常清晰。
二: 开始Flutter代码
2.1.1: HelloWorld 需求: 在界面中心显示一个helloWorld
2.1.2: HelloWorld代码实现
import 'package:flutter/material.dart';
main(List<String> args) {
runApp(Text("hello world", textDirection: TextDirection.ltr));
}
通过上面的代码已经实现了在界面上显示HelloWorld
- 但是没有居中、字体也有点小
上面的代码我们有一些比较熟悉、有一些是不清楚是什么:
- 比如我们知道Dart程序的入口都是main函数、而Flutter是Dart编写的、所以入口也是main函数。
- 但是我们导入的Material是什么呢?
- 另外、我们在main函数中调用了一个runApp()函数又是什么呢?
下面我们进行上面的代码进行分析
2.2. 代码分析
2.2.1: runApp 和 Widget
runApp是Flutter内部提供的一个函数、当我们启动一个Flutter应用程序时就是从调用这个函数开始的。
- 我们可以点到runApp的源代码、查看到该函数
- 我们暂时不分析具体的源码、
void runApp(Widget app) { .....省略代码 }
> 该函数让我们传入一个Widget?
我们先说Widget的翻译:
- Widget 在国内有很多翻译:
- 做过Android、iOS等开发的人群、喜欢翻译为控件
- 做过Vue、React等开发的人群、喜欢将它翻译为组件
- 如果我们使用Google、Widget翻译过来应该是小部件
- 没有哪一个翻译是不正确的、看个人见解吧、我就姑且翻译成小部件或者组件吧
> Widget到底是什么东西呢?
- 我们学习Flutter、从一开始就可以有一个基本的认识、Flutter中万物皆Widget(万物皆可盘)
- 在我们iOS或者Android开发中、我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等。
- 但是在Flutter中、这些东西都是不同的widget而已。
- 也就是我么整个应用程序中所看到的内容几乎都是Widget、甚至是内边距的设置、我们也需要使用一个叫Paddig的Widget来做。
> runApp 函数让我们传入的是一个Widget:
- 但是我们现在没有Widget、怎么办呢?
- 我们可以导入Flutter默认已经给我们提供的Material库、来使用其中的很多内置Widget。
2.2.2: Material设计风格
material 是什么呢?
- material是Google公司推行的一套设计风格、或者叫设计语言、设计贵法等
- 里面有非常多的设计规范、比如颜色、文字的排版、响应动画与过度、填充等。
- 在Flutter中高度集成了Material风格的Widget;
-在我们的应用中、我们可以直接使用这些Widget来创建我们的应用Text小部件分析
- 我们可以使用Text小部件来完成文字的显示
- 我们发现Text小部件继承自StatelessWidget、StatelessWidget继承自Widget;
-所以我们可以将Text小部件传入到runApp函数中- 属性非常多、但是我们已经学习了Dart语法、所以你会发现只有this.data属性是必须传入的。
StatelessWidget简单介绍:
- StatelessWidget继承自Widget:
- 后续会详细介绍它的用法:
2.3: 代码改进
2.3.1: 改进界面样式
- 居中显示、需要使用另外一个Widget, Center
- 文字大一些: 需要给Text文本设置一些样式
代码如下:
runApp(
Center(
child: Text(
'hello world',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 36),
),
)
);
2.3.1: 改进界面机构
目前我们虽然可以显示HelloWorld、但是我们发现最底部的背景是黑色、并且我们的页面并不能结构化
- 正常的App页面应该有一定的机构、比如通常会有导航栏、会有一些背景颜色等。
在开发当中、我们并不需要从零去搭建这种结构化界面、我们可以使用material库、直接使用其中的一些封装好的组件来完成一些结构化的搭建
我们通过以下的代码来实现:
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('lishengbing'),
),
body: Center(
child: Text(
'hello world',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 38),
),
),
),
)
);
效果如图
在最外层包装了一个MaterialApp
- 这意味着整个应用我们都会采用MaterialApp风格的一些东西、方便我们对应用的设计、并且目前我们使用了其中两个属性。
title: 这个是定义在Android系统中打开多任务窗口时显示的标题、暂时可以不写- home: 是该应用启动时显示的页面、我们传入一个Scaffold;
scaffold是什么呢?
- 翻译过来是脚手架、脚手架的作用就是搭建页面的基本结构;
- 所以我们给MaterialApp的home属性传入了一个scaffold对象、作为启动显示的Widget。
- Scaffold也有一些属性、比如appBar和body;
- appBar:是用于设计导航栏的、我们传入了一个title属性;
- body: 是页面的内容部分、我们传入了之前已经创建好的Ceter中包裹的一个Text的Widget;
2.3.3: 进阶案例实现
我们可以让外界中存在更多的元素:
写到这里、你可能已经发现嵌套太多了、不要着急、我们后面会对代码重构的
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('首页'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: true,
onChanged: (value) => print('hello world')),
Text(
'同意协议',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 40),
)
],
),
),
),
)
);
2.4: 代码重构
2.4.1: 创建自己的Widget
很多学习Flutter的人、都会被Flutter的嵌套劝退、当代码嵌套过多时、结构很容易看不清晰
这里有两点我先说说明一下
- 1: Flutter 整个开发过程中、就是形成了一个Widget树🌲、所以形成嵌套是很正常的。
- 2: 关于Flutter的代码缩进、更多开发中我们使用的是2个空格(前端开发2个空格居多、你喜欢4个空格也米有关系)
但是、我们开发一个这么简单的程序就出现如此多的嵌套、如果应用程序更复杂呢?
- 我们可以对我们的代码进行封装、将它们封装到自己的Widget中、创建自己的Widget
如何创建自己的Widget呢
- 在Flutter开发中、我们可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
- StatelessWidget: 没有状态改变的Widget、通常这种Widget仅仅是做一些展示工作使用而已;
- StatefulWidget: 需要保持状态、并且可能出现状态改变的Widget;
在上面的案例中对代码的重构、我们使用StatelessWidget即可、所以我们接下来学习一下如何利用StatelessWidget来对我们接下来学习一下如何利用StatelessWidget来对我们的代码进行重构;`
StatefulWidget我们放到后面的一个案例中来学习;
2.4.2: StatelessWidget
StatelessWidget 通常是一些没有状态(State, 也可以理解为data)需要维护的Widget
- 它们的数据通常是直接写死(放在Widget中的数据)、必须被定义为final、为什么呢?我在下一个章节讲解StatefulWidget会讲到;
- 从parent widget中传入的而且一旦传入就不可以修改。
- 从InheritedWidget获取来使用的数据(这个放到后面会说明)
我们来看一下创建一个StatelessWidget的格式:
- 1: 让自己创建的Widget继承自StatelessWidget;
- 2: StatelessWidget包含一个必须重写的方法:build方法
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
// <返回我么的Widget要渲染的Widget、比如一个Text Widget>
return null;
}
}
build 方法的解析:
- Flutter 在拿到我们自己创建的StatelessWidget时、就会执行它的build方法
- 我们需要在build方法中告诉Flutter、我们的Widget希望渲染什么元素、比如一个Text Widget。
- StatelessWidget没办法主动去执行build方法、当我们使用的数据发生改变时、build方法会被重新执行。
build方法什么情况下被执行呢?
- 1: 当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时)
- 2: 当我们的父Widget(parent Widget)发生改变时、子Widget会被重新构建;
- 3: 如果我们的Widget依赖于InheritedWidget的一些数据、InheritedWidget数据发生改变时;
2.4.3: 重构案例代码
现在我们就可以通过StatelessWidget来对我们的代码进行重构了
- 因为我们的整个代码都是一些数据展示、没有数据的改变、使用StatelessWidget即可
- 另外、为了体现更好的封装性、我对代码进行了两层的拆分、让代码结构看起来更加清晰;(具体的拆分方式、我会在后面的案例中不断的体现出来、目前我们先拆分两层);
重构代码如下:
main(List<String> args) {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("学习自定义StatelessWidget"),
),
body: HomeContent(),
),
);
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: true,
onChanged: (value) => print('点击了Checkbox')),
Text(
'同意协议3',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 50),
)
],
),
);
}
}
三:案例练习
3.1: 案例最终效果
我们先来看一下案例的最终展示效果;
- 这个效果中我们会使用很多没有接触的Widget
- 没有关系、后面这些常用的Widget我会一个个讲解
这个案例最主要的目的还是让大家更加熟悉Flutter的开发模式以及自定义Widget的封装过程。
3.2: 自定义的Widget
main(List<String> args) {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.brown
),
home: Scaffold(
appBar: AppBar(
title: Text("iPhone 11 介绍"),
),
body: HomeContent3(),
),
);
}
}
class HomeContent3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
child: ListView(
children: <Widget>[
ProductItem("iphone 11 pro", "更具美感的沉浸式界面。我们利用摄像头宽广的视角,让你可以看到取景框外的画面,而且轻点一下也能将它拍下来。这让你可以无阻隔地融于场景之中,更加专注地拍摄。", "https://www.apple.com/cn/iphone-11-pro/images/overview/camera/camera_redesigned_ui__sn27i66q2f62_large_2x.jpg"),
ProductItem("iphone 11", "全新双摄系统取景范围更大,可将你的所见所爱尽收画面之中。iPhone 迄今最快的芯片和从早用到晚的电池续航,让你能做的事更多,却无需频频充电。而有了 iPhone 最高的视频画质,你所记录的那些美好时分,也会更加鲜活。", "https://www.apple.com/v/iphone-11/a/images/overview/design-gallery/design_gallery_2_purple__eztcnbruuiy6_large_2x.jpg"),
ProductItem("Mac book Air", "这款 Mac 中的万人迷,注定再一次让人倾心。新款 MacBook Air 更薄、更轻,拥有金色、银色和深空灰色三种外观,配备加入了原彩显示技术的绚丽视网膜显示屏、触控 ID、新一代键盘,以及力度触控板。它标志性的楔形机身由 100% 再生铝金属打造,堪称 Mac 中的环保典范1。此外,它的电池续航能满足一天所需,是一个可以随身而行、事事胜任的好帮手。", "https://www.apple.com/v/mac/home/ai/images/overview/hero/macbook_air__csdfieli984m_large_2x.jpg")
],
),
);
}
}
class ProductItem extends StatelessWidget {
final String title;
final String desc;
final String imageURL;
ProductItem(this.title, this.desc, this.imageURL);
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
padding: EdgeInsets.fromLTRB(20, 0, 20, 20),
decoration: BoxDecoration(
border: Border.all(),
),
child: Column(
children: <Widget>[
SizedBox(height: 30,),
Text(title, style: TextStyle(fontSize: 18)),
SizedBox(height: 10,),
Text(desc, style: TextStyle(fontSize: 15)),
SizedBox(height: 5,),
Image.network(imageURL)
],
),
);
}
}