带你读《Flutter技术入门与实战》之二:Flutter基础知识

简介: 本书的定位适合小白程序员,入门加实战,既有基础知识,又有丰富示例,包括详细的操作步骤,实操性强。由于Flutter大量使用组件,所以对组件的讲解很详细,包括基本概念、属性及代码示例。每个组件都配有小例子,力求精简,还提供了配套网站提供完整代码,复制完整代码就可以立即看到效果。在轻松掌握基础知识的同时快速进入实战。

点击查看第一章
点击查看第三章

第2章

Flutter基础知识
在创建了第一个Flutter程序后,我们还需要继续补充Flutter的基础知识。在后面的章节中讲解组件、布局、动画、装饰等时都需要用到这些基础知识。
本章将围绕以下几个知识点展开:
□入口程序
□Material Design
□Flutter主题
□无状态组件和有状态组件
□使用包资源
□Http请求

2.1 入口程序

每一个Flutter项目的/lib目录下都有一个main.dart文件,打开该文件,里面应该有一个main()函数。Flutter使用Dart语言开发,而在Dart语言中,main()函数是Dart程序的入口,也就是说,Flutter程序在运行的时候,第一个执行的函数就是main()函数。如下面的代码所示:
void main() => runApp(Widget app);
如果你是第一次接触Dart语言,可能会对上面的语法感到陌生,这是Dart语言特有的速写形式,将其展开后,完整代码如下所示:
void main() {
return runApp(Widget app);
}
从上面的代码中可以看到,main()函数中只调用runApp函数,使用runApp函数可以将给定的根组件填满整个屏幕。你可能会有疑问,为什么一定要使用runApp函数?如果不调用runApp函数,项目也可以正常执行,但是屏幕上什么都不会显示。Flutter是Dart语言的移动应用框架,runApp函数就是Flutter框架的入口,如果不调用runApp函数,那你执行的就是一个Dart控制台应用。更多关于Dart语言的细节,会在下面第3章“Dart语言简述”专门讲解。

2.2 Material Design设计风格

每一个.dart文件的第一行几乎都会导入flutter/material.dart包,这个包是Flutter实现Material Design设计风格的基础包,里面有文本输入框(Text)、图标(Icon)、图片(Image)、行排列布局(Row)、列排列布局(Column)、Decoration(装饰器)、动画等组件,大家可以将它们理解为网页中的按钮、标题、选项框等组件库。第一行代码如下所示:
import 'package:flutter/material.dart';
那么Material Design又是什么呢?是谷歌推出的一套视觉设计语言。比如有的App可以换皮肤,而每一套皮肤就是一种设计语言,有古典风、炫酷风、极简风,等等,而Material Design就是谷歌风。Flutter采用的就是Material Design风格。

2.3 Flutter主题

为了在整个应用中使用同一套颜色和字体样式,可以使用“主题”这种方式。定义主题有两种方式:全局主题,或使用Theme来定义应用程序局部的颜色和字体样式。事实上,全局主题只是由应用程序根MaterialApp创建的主题(Theme)。
定义一个主题后,就可以在我们自己的Widget中使用它,Flutter提供的Material Widgets将使用主题为AppBars、Buttons、Checkboxes等设置背景颜色和字体样式。

2.3.1 创建应用主题

创建主题的方法是将ThemeData提供给MaterialApp构造函数,这样就可以在整个应用程序中共享包含颜色和字体样式的主题。ThemeData的主要属性如表2-1所示。

image.png
image.png

如果没有提供主题,Flutter将创建一个默认主题。主题数据的示例代码如下:
new MaterialApp(
title: title,
theme: new ThemeData(

brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
accentColor: Colors.cyan[600],

),
);

2.3.2 局部主题

如果我们想在应用程序的某一部分使用特殊的颜色,那么就需要覆盖全局的主题。有两种方法可以解决这个问题:创建特有的主题数据或扩展父主题。
1.创建特有的主题数据
实例化一个ThemeData并将其传递给Theme对象,代码如下:
new Theme(
//创建一个特有的主题数据
data: new ThemeData(

accentColor: Colors.yellow,

),
child: new FloatingActionButton(

onPressed: () {},
child: new Icon(Icons.add),

),
);
2.扩展父主题
扩展父主题时无须覆盖所有的主题属性,我们可以通过使用copyWith方法来实现,代码如下:
new Theme(
//覆盖accentColor为Colors.yellow
data: Theme.of(context).copyWith(accentColor: Colors.yellow),
child: new FloatingActionButton(

onPressed: null,
child: new Icon(Icons.add),

),
);

2.3.3 使用主题

主题定义好后就可以使用它了。首先,函数Theme.of(context)可以通过上下文来获取主题,方法是查找最近的主题,如果找不到就会找整个应用的主题。
下面来看一个简单的示例,应用的主题颜色定义为绿色,界面中间再加一个带有背景色的文本。
完整的例子代码如下所示:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
runApp(new MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {

final appName = '自定义主题';

return new MaterialApp(
  title: appName,
  theme: new ThemeData(
    brightness: Brightness.light,//应用程序整体主题的亮度
    primaryColor: Colors.lightGreen[600],//App主要部分的背景色
    accentColor: Colors.orange[600],//前景色(文本、按钮等)
  ),
  home: new MyHomePage(
    title: appName,
  ),
);

}
}

class MyHomePage extends StatelessWidget {
final String title;

MyHomePage({Key key, @required this.title}) : super(key: key);

@override
Widget build(BuildContext context) {

return new Scaffold(
  appBar: new AppBar(
    title: new Text(title),
  ),
  body: new Center(
    child: new Container(
      //获取主题的accentColor
      color: Theme.of(context).accentColor,
      child: new Text(
        '带有背景颜色的文本组件',
        style: Theme.of(context).textTheme.title,
      ),
    ),
  ),
  floatingActionButton: new Theme(
    //使用copyWith的方式获取accentColor
    data: Theme.of(context).copyWith(accentColor: Colors.grey),
    child: new FloatingActionButton(
      onPressed: null,
      child: new Icon(Icons.computer),
    ),
  ),
);

}
}
自定义主题的效果如图2-1所示。

image.png

2.4 无状态组件和有状态组件

无状态组件(StatelessWidget)是不可变的,这意味着它们的属性不能改变,所有的值都是最终的。
有状态组件(StatefulWidget)持有的状态可能在Widget生命周期中发生变化。实现一个StatefulWidget至少需要两个类:
□一个StatefulWidget类。
□一个State类。StatefulWidget类本身是不变的,但是State类在Widget生命周期中始终存在。
Flutter的官方给出一个有状态组件的示例,点击右下角的+号按钮,应用界面中间的数字会加1,如图2-2所示。
这个示例有几个关键的部分,解析如下。
示例代码中MyHomePage必须继承自StatefulWidget类,如下所示:

image.png

class MyHomePage extends StatefulWidget
重写createState方法,如下所示:
@override
MyHomePageState createState() => new _MyHomePageState();
状态类必须继承自State类,如下所示:
class _MyHomePageState extends State
定义一个普通变量_counter作为计数器变量,调用setState方法来控制这个变量的值的变化,如下所示:
int _counter = 0;

void _incrementCounter() {
setState(() {

//计数器变量
_counter++;

});
}
完整的示例代码如下所示:
import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

//MyApp不需要做状态处理,所以此组件继承StatelessWidget即可
class MyApp extends StatelessWidget {
// 这个组件是整个应用的主组件
@override
Widget build(BuildContext context) {

return new MaterialApp(
  title: 'Flutter Demo',
  theme: new ThemeData(
    //自定义主题
    primarySwatch: Colors.blue,
  ),
  home: new MyHomePage(title: 'Flutter Demo Home Page'),
);

}
}

//主页需要继承自StatefulWidget
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

//标题
final String title;

//必须重写createState方法
@override
_MyHomePageState createState() => new _MyHomePageState();
}
//状态类必须继承State类,注意后面需要指定为
class _MyHomePageState extends State {
int _counter = 0;//计数器

void _incrementCounter() {

//调用State类里的setState方法来更改状态值,使得计数器加1
setState(() {
  //计数器变量,每次点击让其加1
  _counter++;
});

}

@override
Widget build(BuildContext context) {

return new Scaffold(
  appBar: new AppBar(
    title: new Text(widget.title),
  ),
  //居中布局
  body: new Center(
    
    //垂直布局
    child: new Column(
      //主轴居中对齐
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        new Text(
          'You have pushed the button this many times:',
        ),
        new Text(
          '$_counter',//绑定计数器的值
          style: Theme.of(context).textTheme.display1,
        ),
      ],
    ),
  ),
  floatingActionButton: new FloatingActionButton(
    onPressed: _incrementCounter,//按下+号按钮调用自增函数
    tooltip: 'Increment',
    child: new Icon(Icons.add),
  ), 
);

}
}

2.5 使用包资源

Flutter包类似于Java语言里的jar包,由全球众多开发者共同提供第三方库。例如,网络请求(http)、自定义导航/路由处理(fluro)、集成设备API(如url_launcher&battery) 以及第三方平台SDK(如Firebase)等。这使得开发者可以快速构建应用程序,而无须从头造轮子。
1.包仓库
所有包(package)都会发布到Dart的包仓库里,如图2-3所示,输入你想使用的包后点击搜索即可。
提示:包仓库地址为:https://pub.dartlang.org

2.包使用示例
接下来使用url_launcher这个包来详解讲解第三方包的使用,步骤如下。
步骤1:打开pubspec.yaml文件,在dependencies下添加包的名称及版本,如图2-4箭头指向的内容所示。

image.png

image.png

步骤2:点击Packages get命令来获取工程配置文件中所添加的引用包,或者打开命令行窗口执行flutter packages get命令,如图2-5所示。
注意:在更新包资源的过程中注意观察控制台消息,可能有版本错误、网络问题,这些都会导致更新失败。

image.png


步骤3:打开main.dart文件,导入url_launcher.dart包:
import 'package:url_launcher/url_launcher.dart';
步骤4:这时就可以使用launch方法来打开url地址了:
const url = ' https://www.baidu.com';
launch(url);
完整的main.dart代码如下所示:
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {

return new MaterialApp(
  title: '使用第三方包示例',
  home: new Scaffold(
    appBar: new AppBar(
      title: new Text('使用第三方包示例'),
    ),
    body: new Center(
      child: new RaisedButton(
        onPressed: () {
          //指定url并发起请求
          const url = 'https://www.baidu.com';
          launch(url);
        },
        child: new Text('打开百度'),
      ),
    ),
  ),
);

}
}
步骤5:启动示例,打开界面如图2-6所示。
点击“打开百度”按钮,页面会跳转至百度页面,如图2-7 所示。

image.png

2.6 Http请求

HTTP协议通常用于做前后端的数据交互。Flutter请求网络有两种方法,一种是用Http请求,另一种是用HttpClient请求。
1. Http请求方式
在使用Http方式请求网络时,需要导入http包。如下所示:
import 'package:http/http.dart' as http;
请看下面的完整示例代码,示例中发起了一个http的get请求,并将返回的结果信息打印到控制台里:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {

return new MaterialApp(
  title: 'http请求示例',
  home: new Scaffold(
    appBar: new AppBar(
      title: new Text('http请求示例'),
    ),
    body: new Center(
      child: new RaisedButton(
        onPressed: () {

          var url = 'http://httpbin.org/';
          //向http://httpbin.org/发送get请求
          http.get(url).then((response) {
            print("状态: ${response.statusCode}");
            print("正文: ${response.body}");
          });

        },
        child: new Text('发起http请求'),
      ),
    ),
  ),
);

}
}
请求界面如图2-8所示。

image.png


点击“发起http请求”按钮,程序开始请求指定的url,如果服务器正常返回数据,则状态码为200。控制台输出内容如下:
Performing hot reload...
Syncing files to device iPhone X...
Reloaded 1 of 509 libraries in 452ms.
flutter: 状态: 200
flutter: 正文:

image.png

注意:服务器返回状态200,同时返回正文。完整的正文远不只这些内容,你可以自己测试此示例,并查看控制台的输出消息。

2. HttpClient请求方式
在使用HttpClient方式请求网络时,需要导入io及convert包,如下所示:
import 'dart:convert';
import 'dart:io';
请看下面的完整示例代码,示例中使用HttpClient请求了一条天气数据,并将返回的结果信息打印到控制台里。具体请求步骤看代码注释即可。
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

//获取天气数据
void getWeatherData() async {

try {
  //实例化一个HttpClient对象
  HttpClient httpClient = new HttpClient();

  //发起请求
  HttpClientRequest request = await httpClient.getUrl(
      Uri.parse("http://t.weather.sojson.com/api/weather/city/101030100"));

  //等待服务器返回数据
  HttpClientResponse response = await request.close();

  //使用utf8.decoder从response里解析数据
  var result = await response.transform(utf8.decoder).join();
  //输出响应头
  print(result);

  //httpClient关闭
  httpClient.close();

} catch (e) {
  print("请求失败:$e");
} finally {

}

}

@override
Widget build(BuildContext context) {

return MaterialApp(
  title: 'httpclient请求',
  home: Scaffold(
    appBar: AppBar(
      title: Text('httpclient请求'),
    ),
    body: Center(
      child: RaisedButton(
        child: Text("获取天气数据"),
        onPressed: getWeatherData,
      ),
    ),
  ),
);

}
}
请求界面如图2-9所示。

image.png

点击“获取天气数据”按钮,程序开始请求指定的url,如果服务器正常返回数据,则状态码为200。控制台输出内容如下:
Performing hot reload...
Syncing files to device iPhone X...
Reloaded 1 of 419 libraries in 412ms.
flutter: {"time":"2018-11-30 08:09:00","cityInfo":{"city":"天津市","cityId":
"101030100","parent":"天津","updateTime":"07:56"},"date":"20181130","message":
"Success!", "status":200,"data":{"shidu":"84%","pm25":30.0,"pm10":79.0,"quality":"良","wendu":"1","ganmao":"极少数敏感人群应减少户外活动","yesterday":{"date":"29日星期四","sunrise":"07:08","high":"高温 9.0℃","low":"低温 0.0℃","sunset":"16:50","aqi":86.0,"fx":"东风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"}, "forecast": [{"date":"30日星期五","sunrise":"07:09","high":"高温 10.0℃","low":"低温 1.0℃","sunset":"16:50","aqi":53.0,"fx":"西风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"01日星期六","sunrise":"07:10","high":"高温 10.0℃","low":"低温4.0℃", "sunset":"16:49","aqi":94.0,"fx":"东风","fl":"<3级","type":"阴","notice":"不要被阴云遮挡住好心情"},{"date":"02日星期日","sunrise":"07:11","high":"高温 1<…>
注意:返回的数据是JSON格式,所以后续还需要做JSON处理。另外还需要使用utf8.decoder从response里解析数据。

如果请求里需要带参数,可以在URI里增加查询参数,具体的请求地址和参数要根据实际需要编写,代码如下所示:
Uri uri=Uri(scheme: "https", host: "t.weather.sojson.com", queryParameters: {

"_id": 26,
"city_code": "101030100",//接口需要的city_code
"city_name": "天津"

});

相关文章
|
5月前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
215 0
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
3天前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
18 7
|
5月前
|
前端开发 C++ 容器
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
Flutter-完整开发实战详解(一、Dart-语言和-Flutter-基础)(1)
|
2月前
|
Dart 前端开发 JavaScript
Flutter&Dart-异步编程Future、Stream极速入门
Flutter&Dart-异步编程Future、Stream极速入门
58 4
Flutter&Dart-异步编程Future、Stream极速入门
|
2月前
|
SQL 分布式计算 大数据
Flutter技术实践问题之Flutter应用过程中的基础建设如何解决
Flutter技术实践问题之Flutter应用过程中的基础建设如何解决
24 10
|
2月前
|
新零售 前端开发 小程序
Flutter技术实践问题之基于Flutter的Canvas的应用优势如何解决
Flutter技术实践问题之基于Flutter的Canvas的应用优势如何解决
26 2
|
2月前
|
Web App开发 新零售 前端开发
Flutter技术实践问题之阿里集团内Flutter体系化建设如何解决
Flutter技术实践问题之阿里集团内Flutter体系化建设如何解决
29 1
|
2月前
|
Kubernetes Cloud Native 搜索推荐
探索云原生技术:Kubernetes入门与实践打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第31天】云原生技术正改变着应用开发和部署的方式。本文将带你了解云原生的基石——Kubernetes,通过实际的代码示例,从安装到部署一个简单的应用,让你迅速掌握Kubernetes的核心概念和操作方法。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你进入云原生世界的桥梁。
|
3月前
|
开发框架 Dart Java
Flutter入门进阶之旅(一)-初识Flutter
Flutter 的目标是用来创建高性能、高稳定性、高帧率、低延迟的 Android 和 iOS 应用。并且开发出来的应用在不同的平台用起来跟原生应用具有一样的体验。不同的平台的原生体验应该得到保留,让该应用看起来同整个系统更加协调。不同平台的滚动操作、字体、图标 等特殊的特性 应该和该平台上的其他应用保持一致,让用户感觉就像操作原生应用一样。
41 1
|
3月前
|
Dart 搜索推荐
Flutter入门进阶之旅(二)Hello Flutter
好像几乎我们学习或者掌握任何一门编程语言都是Hello word开始的,本篇博文做为Flutter入门进阶的第一篇分享,我们也从最简单的Hello world开始,至于Flutter开发环境的配置,跟关于Dart语言的介绍,不是该专栏要讲解的内容,我就不详细做介绍了,读者可自行google或者百度了解一下。
45 0
下一篇
无影云桌面