写给React Native程序员的Flutter入门指南

发表于 5年以前  | 总阅读数:2996 次

本文面向希望基于现有的 React Native 的知识结构使用 Flutter 开发移动端应用的开发者。如果你已经对 RN 的框架有所了解,那么你可以通过这个文档入门 Flutter 开发。

本文可以当做查询手册使用,里面涉及到的问题基本上可以满足需求。

本文结构如下:

1.针对 JavaScript 开发者的 Dart 介绍(上)

2.基本知识(上)

3.项目结构和资源(上)

4.Flutter widgets(上)

5.视图(上)

6.布局(上)

7.风格化(上)

8.状态管理(下)

9.道具(下)

10.本地存储(下)

11.路径(下)

12.手势检测和触摸事件处理(下)

13.发起 HTTP 网络请求(下)

14.输入表单(下)

15.平台相关代码(下)

16.调试(下)

17.动画(下)

18.React Native 和 Flutter 小部件对等的组件(下)

------一、针对 JavaScript 开发者的 Dart 介绍

和 React Native 一样,Flutter 使用 reactive 风格的视图。然而,RN 需要被转译为本地对应的 widget,而 Flutter 是直接编译成本地原生代码。Flutter 可以控制屏幕上的每一个像素,如此可以避免由于使用 JavaScript Bridge 导致的性能问题。

Dart 学习起来非常简单而且有如下特性:

  • 它针对 web 服务和移动应用开发提供了一种开源的,可扩展的编程语言。

  • 它提供了一种面向对象的单继承语言,使用 C 语言风格的语法并且可通过 AOT 编译为本地代码。

  • 可转译为 JavaScript 代码。

  • 支持接口和抽象类。

下面的几个例子解释了 JavaScript 和 Dart 的区别。

1.1 入口函数

JavaScript 并没有预定义的入口函数。

// JavaScript
function startHere() {
  // Can be used as entry point
  //这里可以当做入口函数
}

在 Dart 里,每个应用程序必须有一个最顶级的 main() 函数,该函数作为应用程序的入口函数。

// Dart
main() {
}

可以在 DartPad 查看效果。

页面链接:https://dartpad.cn/0df636e00f348bdec2bc1c8ebc7daeb1

1.2 在控制台打印输出

在 Dart 中如果需要在控制台进行输出,调用 print()。

// JavaScript
console.log('Hello world!');
// Dart
print('Hello world!');

1.3 变量

Dart 是类型安全的,它结合静态类型检查和运行时检查来保证变量的值总是和变量的静态类型相匹配。虽然类型是语法要求,有些类型标注也并不是必须要填的,因为 Dart 使用类型推断。

1.3.1 创建变量并赋值

在 JavaScript 中,变量是无法指定类型的。

在 Dart 中,变量要么被显式定义类型,要么系统会自动判断变量的类型。

// JavaScript
var name = 'JavaScript';
// Dart
String name = 'dart'; // Explicitly typed as a string.

String name = 'dart'; // 显式声明为字符串。

var otherName = 'Dart'; // Inferred string.

var otherName = 'Dart'; // 推断为字符串。

// Both are acceptable in Dart.

// 两种定义方式在 Dart 中都可以。

如果想了解更多相关信息,请转向该页面 Dart’s Type System。

页面链接:https://dart.dev/guides/language/sound-dart

1.3.2 默认值

在 JavaScript 中, 未初始化的变量是 ‘undefined’。

在 Dart 中,未初始化的变量会有一个初始值 null。因为数字在 Dart 是对象,甚至未初始化的数字类型的变量也会是 null。

// JavaScript
var name; // == undefined
// Dart
var name; // == null
int x; // == null

如果想了解更多详细内容,请查看这个文档 variables。

文档链接:https://dart.dev/guides/language/language-tour#variables

1.4 检查 null 或者零值

在 JavaScript 中,1 或者任何非空对象都相当于 true。

// JavaScript
var myNull = null;
if (!myNull) {
  console.log('null is treated as false');
}
var zero = 0;
if (!zero) {
  console.log('0 is treated as false');
}

在 Dart 中,只有布尔类型值 true 才是 true。

// Dart
var myNull = null;
if (myNull == null) {
  print('use "== null" to check null');
}
var zero = 0;
if (zero == 0) {
  print('use "== 0" to check zero');
}

1.5 函数

Dart 和 JavaScript 中的函数很相似。最大的区别是声明格式。

// JavaScript
function fn() {
  return true;
}
// Dart
fn() {
  return true;
}
// can also be written as
bool fn() {
  return true;
}

如果想了解更多相关信息,请转向该页面 functions。

页面链接:https://dart.dev/guides/language/language-tour#functions

1.6 异步编程

1.6.1 Futures

和 JavaScript 类似,Dart 支持单线程。在 JavaScript 中, Promise 对象代表异步操作的完成或者失败。

Dart 使用 Future 对象来实现该机制。

// JavaScript
class Example {
  _getIPAddress() {
    const url = 'https://httpbin.org/ip';
    return fetch(url)
      .then(response => response.json())
      .then(responseJson => {
        const ip = responseJson.origin;
        return ip;
      });
  }
}

function main() {
  const example = new Example();
  example
    ._getIPAddress()
    .then(ip => console.log(ip))
    .catch(error => console.error(error));
}

main();
// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class Example {
  Future<String> _getIPAddress() {
    final url = 'https://httpbin.org/ip';
    return http.get(url).then((response) {
      String ip = jsonDecode(response.body)['origin'];
      return ip;
    });
  }
}

main() {
  final example = new Example();
  example
      ._getIPAddress()
      .then((ip) => print(ip))
      .catchError((error) => print(error));
}

如果想了解更多相关信息,请参考 Futures 的相关文档。

 Futures 文档链接:https://dart.dev/tutorials/language/futures

1.6.2 async 和 await

async 函数声明定义了一个异步函数。

在 JavaScript 中, async 函数返回一个 Promise。await 操作符用于等待 Promise。

// JavaScript
class Example {
  async _getIPAddress() {
    const url = 'https://httpbin.org/ip';
    const response = await fetch(url);
    const responseJson = await response.json();
    const ip = responseJson.origin;
    return ip;
  }
}

async function main() {
  const example = new Example();
  try {
    const ip = await example._getIPAddress();
    console.log(ip);
  } catch (error) {
    console.error(error);
  }
}

main();

在 Dart 中,async 函数返回一个 Future,而函数体会在未来执行。await 操作符用于等待 Future。

// Dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class Example {
  Future<String> _getIPAddress() async {
    final url = 'https://httpbin.org/ip';
    final response = await http.get(url);
    String ip = jsonDecode(response.body)['origin'];
    return ip;
  }
}

main() async {
  final example = new Example();
  try {
    final ip = await example._getIPAddress();
    print(ip);
  } catch (error) {
    print(error);
  }
}

如果想了解更多相关信息,请参考 async and await 的相关文档。

文档链接:https://dart.dev/guides/language/language-tour#asynchrony-support

二、基本知识

2.1 如何创建一个 Flutter 应用?

如果要使用 React Native 创建应用,你需要在命令行里运行 create-react-native-app。

$ create-react-native-app <projectname>

要在 Flutter 中创建应用,完成下面其中一项即可:

  • 在命令行中运行命令 flutter create。不过要提前确认 Flutter SDK 已经在系统环境变量 PATH 中定义。

  • 使用带有 Flutter 和 Dart 插件的 IDE。

$ flutter create <projectname>

如果想要了解更多内容,详见 Getting Started,在该页面会手把手教你创建一个点击按钮进行计数的应用。创建一个 Flutter 项目就可以构建 Android 和 iOS 设备上运行应用所需的所有文件。

2.2 我如何运行应用呢?

在 React Native, 你可以在项目文件夹中运行 npm run 或者 yarn run。

你可以通过如下几个途径运行 Flutter 应用程序:

  • 在项目根目录运行 flutter run。

  • 在带有 Flutter 和 Dart 插件的 IDE 中使用 “run” 选项。

你的应用程序会在已连接的设备、iOS 模拟器或者 Android 模拟器上运行。

如果想了解更多相关信息,可以参考 Flutter 的 Getting Started 相关文档。

2.3 如何导入 widget

在 React Native 中,你需要导入每一个所需的组件。

//React Native
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

在 Flutter 中,如果要使用 Material Design 库里的 widget,导入 material.dart 包。如果要使用 iOS 风格的 widget,导入 Cupertino 库。如果要使用更加基本的 widget,导入 Widget 库。或者,你可以实现自己的 widget 库并导入。

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/my_widgets.dart';

无论你导入哪个库,Dart 仅仅引用你应用中用到的 widget。

如果想了解更多相关信息,可以参考 Flutter Widgets Catalog。

页面链接:https://flutter-io.cn/docs/development/ui/widgets

2.4 在 Flutter 里有没有类似 React Native 中 “Hello world!” 应用程序?

在 React Native,HelloWorldApp 继承自 React.Component 并且通过返回 view 对象实现了 render 方法。

// React Native
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Hello world!</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});

在 Flutter 中,你可以使用核心 widget 库中的 Center 和 Text widget 创建对应的 “Hello world!” 应用程序。Center widget 是 widget 树中的根,而且只有 Text 一个子 widget。

// Flutter
import 'package:flutter/material.dart';

void main() {
  runApp(
    Center(
      child: Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}

下面的图片展示了 Android 和 iOS 中的基本 Flutter “Hello world!” 应用程序的界面。

    Android  

 iOS

现在大家已经明白了最基本的 Flutter 应用,接下来会告诉大家如何利用 Flutter 丰富的 widget 库来创建主流的华丽的应用程序。

2.5 我如何使用 widget 并且把它们封装起来组成一个 widget 树?

在 Flutter 中,几乎任何元素都是 widget。

widget 是构建应用软件用户界面的基本元素。你可以将 widget 按照一定的层次组合,成为 widget 树。每个 widget 内嵌在父 widget 中,并且继承了父 widget 的属性。甚至应用程序本身就是一个 widget。并没有一个独立的应用程序对象。反而 root widget 充当了这个角色。

一个 widget 可以定义:

  • 一个结构化的元素 - 类似按钮或者菜单

  • 一个风格化的元素 - 类似字体或者颜色方案

  • 布局元素 - 类似填充区或者对齐元素

下面的示例展示了使用 Material 库里 widget 实现的 “Hello world!” 应用程序。在这个示例中,该 widget 树是包含在 MaterialApproot widget 里的。

// Flutter
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
 widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text('Hello world'),
        ),
      ),
    );
  }
}

下面的图片为大家展示了通过 Material Design widget 所实现的 “Hello world!” 应用。你可以免费获得比 “Hello world!” 应用更多的功能。

 Android  

 iOS

当编写应用代码的时候,你将用到下述两种 widget :

无状态 widget (StatelessWidget) 就像它的名字一样,是一个没有状态的 widget。无状态 widget 一旦创建,就不会改变。而有状态 widget (StatefulWidget) 会基于接收到的数据或者用户输入的数据动态改变状态。

无状态 widget 和有状态 widget 之间的主要区别是有状态 widget 包含一个 State 对象,会缓存状态数据,并且 widget 树的重构也会携带该数据。因此状态不会丢失。

在简单的或者基本的应用程序中,封装 widget 非常简单,但是随着代码量的增加并且应用程序的功能变得更加复杂,你应该将层级复杂的 widget 封装到函数中或者稍小一些的类。创建独立的函数和 widget 可以让你更好地复用应用中组件。

2.6 如何创建可复用的组件?

在 React Native 中,你可以定义一个类来创建一个可复用的组件然后使用 props 方法来设置或者返回属性或者所选元素的值。在下面的示例中,CustomCard 类在父类中被定义和调用。

// React Native
class CustomCard extends React.Component {
  render() {
    return (
      <View>
        <Text> Card {this.props.index} </Text>
        <Button
          title="Press"
          onPress={() => this.props.onPress(this.props.index)}
        />
      </View>
    );
  }
}

// Usage
<CustomCard onPress={this.onPress} index={item.key} />

在 Flutter 中,定义一个类来创建一个自定义 widget 然后复用这个 widget。你可以定义并且调用函数来返回一个可复用的 widget,正如下面示例中 build 函数所示的那样。

// Flutter
class CustomCard extends StatelessWidget {
  CustomCard({@required this.index, @required 
     this.onPress});

  final index;
  final Function onPress;

  @override
 widget build(BuildContext context) {
    return Card(
      child: Column(
        children: <Widget>[
          Text('Card $index'),
          FlatButton(
            child: const Text('Press'),
            onPressed: this.onPress,
          ),
        ],
      )
    );
  }
}
    ...
// Usage
CustomCard(
  index: index,
  onPress: () { 
    print('Card $index');
  },
)
    ...

在之前的示例,CustomCard 类的构造函数使用 Dart 的曲括号 { } 来表示可选参数 optional parameters。

如果将这些参数设定为必填参数,要么从构造函数中删掉曲括号,或者在构造函数中加上 @required。

下面的截图展示了可复用的 CustomCard 类的示例。

 Android

iOS

三、项目结构和资源

3.1 该从哪开始写代码呢?

从 main.dart 文件开始。当你创建 Flutter 应用的时候会自动生成这个文件。

// Dart

void main(){

 print('Hello, this is the main function.');

}

在 Flutter 中,入口文件是 ’projectname’/lib/main.dart 而程序执行是从 main 函数开始的。

3.2 Flutter 应用程序中的文件是如何组织的?

当你创建一个新的 Flutter 工程的时候,它会创建如下所示的文件夹结构。你可以自定义这个结构,不过这是整个开发的起点。

└ projectname

  ┬

  ├ android      - 包含 Android 相关文件。

  ├ build        - 存储 iOS 和 Android 构建文件。

  ├ ios          - 包含 iOS 相关文件。

  ├ lib          - 包含外部可访问 Dart 源文件。

    ┬

    └ src        - 包含附加源文件。

    └ main.dart  - Flutter 程序入口和新应用程序的起点。

                   当你创建 Flutter 工程的时候会自动生成这些文件。

                   你从这里开始写 Dart 代码

  ├ test         - 包含自动测试文件。

  └ pubspec.yaml - 包含 Flutter 应用程序的元数据。

                   这个文件相当于 React Native 里的 package.json 文件。

3.3 我该把资源文件放到哪并且如何调用呢?

一个 Flutter 资源就是打包到你应用程序里的一个文件并且在程序运行的时候可以访问。Flutter 应用程序可以包含下述几种资源类型:

  • 静态数据 比如 JSON 文件

  • 配置文件

  • 图标和图片 (JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP)

Flutter 使用 pubspec.yaml 文件来确定应用程序中的资源。该文件在工程的根目录。

flutter:
  assets:
    - assets/my_icon.png
    - assets/background.png

assets 确定了需要包含在应用程序中的文件。每个资源都会在 pubspec.yaml 中定义所存储的相对路径。资源定义的顺序没有特殊要求。实际的文件夹(在这里指 assets )也没影响。但是, 由于资源可以放置于程序的任何目录,所以放在 assets 文件夹是比较好的。

在构建期间,Flutter 会将资源放到一个称为 asset bundle 的归档文件中,应用程序可以在运行时访问该文件。当一个资源在 pubspec.yaml 中被声明时,构建进程会查询和这个文件相关的子文件夹路径。这些文件也会被包含在 asset bundle 中。当你为应用程序选择和屏幕显示分辨率相关的图片时,Flutter 会使用 asset variants。

在 React Native,你可以在源码文件夹中通过添加文件来增加一个静态图片并且在代码中引用它。

<Image source={require('./my-icon.png')} />

在 Flutter 中,如果要增加静态图片的话就在 widget 的 build 方法中使用 AssetImage 类。

image: AssetImage('assets/background.png'),

3.4 如何在网络中加载图片?

在 React Native,你可以在 Image 的 source 属性中设置 uri 和所需的尺寸。

在 Flutter 中,使用 Image.network 构造函数来实现通过地址加载图片的操作。

// Flutter

body: Image.network(

          'https://flutter.io/images/owl.jpg',

3.5 我如何安装依赖包和包插件?

Flutter 支持使用开发者向 Flutter 和 Dart 生态系统贡献的代码包。这样可以使大量开发者快速构建应用程序而无需重复造车轮。而平台相关的代码包就被称为包插件。

在 React Native 中,你可以在命令行中运行 yarn add {package-name} 或者 npm install --save {package-name} 来安装代码包。

在 Flutter 中,安装代码包需要按照如下的步骤:

  1. 在 pubspec.yaml 的 dependencies 区域添加包名和版本。下面的例子向大家展示了如何将 google_sign_in 的 Dart package 添加到 pubspec.yaml 中。一定要检查一下 YAML 文件中的空格。因为 空格很重要!
dependencies:

  flutter:

    sdk: flutter

  google_sign_in: ^3.0.3
  1. 在命令行中输入 flutter packages get 来安装代码包。如果使用 IDE,它自己会运行 flutter packages get, 或者它会提示你是不是要运行该命令。

  2. 向下面代码一样在程序中引用代码包:

import 'package:flutter/cupertino.dart';

四、Flutter widgets

在 Flutter 中,你可以基于 widget 打造你自己的 UI,通过 widget 当前的设置和状态会呈现相应的页面效果。

widget 常常通过很多小的,单一功能的 widget 组成,这样的封装往往能实现很棒的效果。比如, Container widget 包含多种 widget,分别负责布局、绘图、位置变化和尺寸变化。Container widget 包括 LimitedBox, ConstrainedBox, Align, Padding, DecoratedBox, 和 Transform widget。与其继承 Container 来实现自定义效果, 不如直接修改这些 widget 来实现效果。

Center widget 是另一个用于控制布局的示例。如果要居中一个 widget, 就把它封装到 Center widget 中,然后使用布局 widget 来进行对齐行、列和网格。这些布局 widget 并不可见。而他们的作用就是控制其它 widget 的布局。如果想搞清楚为什么一个 widget 会有这样的效果,有效的方法是研究它临近的 widget。

五、视图

5.1 与 View 等价容器的是什么?

在 React Native 中, View 是支持 Flexbox 布局、风格化、触摸事件处理和访问性控制的容器。

在 Flutter 中,你可以使用 Widgets 库中的核心布局 widget,比如 Container, Column, Row, 和 Center。

5.2 和 FlatList 或者 SectionList 相对应的是什么?

List 是一个可以滚动的纵向排列的组件列表。

在 React Native 中,FlatList 或 SectionList 用于渲染简单的或者分组的列表。

// React Native

<FlatList

  data={[ ... ]}

  renderItem={({ item }) => <Text>{item.key}</Text>}

/>

ListView 是 Flutter 最常用的滑动 widget。默认构造函数需要一个数据列表的参数。ListView 非常适合用于少量子 widget 的列表。如果列表的元素比较多,可以使用 ListView.builder,它会按需构建子项并且只创建可见的子项。

// Flutter

var data = [ ... ];

ListView.builder(

  itemCount: data.length,

  itemBuilder: (context, int index) {

    return Text(

      data[index],

    );

  },

)

<figure class="Editable-styled" data-block="true" data-editor="b4g4h" data-offset-key="75v8o-0-0" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: 16px;text-align: start;white-space: pre-wrap;background-color: rgb(255, 255, 255);">

Android

iOS

5.3 如何使用 Canvas 绘图?

在 React Native 中,canvas 组件是不可见的,所以需要使用类似 react-native-canvas 这样的组件。

// React Native

handleCanvas = canvas => {

  const ctx = canvas.getContext('2d');

  ctx.fillStyle = 'skyblue';

  ctx.beginPath();

  ctx.arc(75, 75, 50, 0, 2 * Math.PI);

  ctx.fillRect(150, 100, 300, 300);

  ctx.stroke();

};



render() {

  return (

    <View>

      <Canvas ref={this.handleCanvas} />

    </View>

  );

}

在 Flutter 中,你可以使用 CustomPaint 和 CustomPainter 进行绘图。

下面的示例代码展示了如何使用 CustomPaint 进行绘图。它实现了抽象类 CustomPainter,然后将它赋值给 CustomPainter 的 painter 属性。CustomPainter 子类必须实现 paint 和 shouldRepaint 方法。

``` // Flutter

class MyCanvasPainter extends CustomPainter {

  @override

  void paint(Canvas canvas, Size size) {

    Paint paint = Paint();

    paint.color = Colors.amber;

    canvas.drawCircle(Offset(100.0, 200.0), 40.0, paint);

    Paint paintRect = Paint();

    paintRect.color = Colors.lightBlue;

    Rect rect = Rect.fromPoints(Offset(150.0, 300.0), Offset(300.0, 400.0));

    canvas.drawRect(rect, paintRect);

  }

  bool shouldRepaint(MyCanvasPainter oldDelegate) => false;

  bool shouldRebuildSemantics(MyCanvasPainter oldDelegate) => false;

}

class _MyCanvasState extends State {

  @override

 widget build(BuildContext context) {

    return Scaffold(

      body: CustomPaint(

        painter: MyCanvasPainter(),

      ),

    );

  }

}

<figure class="Editable-styled" data-block="true" data-editor="b4g4h" data-offset-key="6ha6d-0-0" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: 16px;text-align: start;white-space: pre-wrap;background-color: rgb(255, 255, 255);">
![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/de019bc7babcf417911cf9b2771c8356)

Android 

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/906ea8e04fb1953a80ba500d7380cf49)

iOS

六、布局



6.1 如何使用 widget 来定义布局属性?

在 React Native 中,大多数布局需要通过向指定的组件传递属性参数进行设置。比如,你可以使用 View 的 style 来设置 flexbox 属性。如果要整理一列的组件,你可以使用如下的属性设置:flexDirection: “column”。

// React Native

<View

  style={{

    flex: 1,

    flexDirection: 'column',

    justifyContent: 'space-between',

    alignItems: 'center'

  }}


在 Flutter 中,布局主要是由专门的 widget 定义的,它们同控制类 widget 和样式属性一起发挥功能。

比如,Column 和 Row widget 接受一个数组的子元素并且分别按照纵向和横向进行排列。

// Flutter

Center(

  child: Column(

    children: [

      Container(

        color: Colors.red,

        width: 100.0,

        height: 100.0,

      ),

      Container(

        color: Colors.blue,

        width: 100.0,

        height: 100.0,

      ),

      Container(

        color: Colors.green,

        width: 100.0,

        height: 100.0,

      ),

    ],

  ),

)


Flutter 在核心 widget 库中提供多种不同的布局 widget。比如Padding, Align, 和 Stack。
</figure>![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/2314b60634d49f19109c51d2c0e0a3f3)


Android  

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/afa86044d4254bafefbf7f2e32689c47)


iOS  

6.2 如何为 widget 分层?  

在 React Native 中,组件可以通过 absolute 划分层次。  

在 Flutter 中使用 Stack widget 将子 widget 进行分层。该 widget 可以将整体或者部分的子 widget 进行分层。  

Stack widget 将子 widget 根据容器的边界进行布局。如果你仅仅想把子 widget 重叠摆放的话,这个 widget 非常合适。  

// Flutter

Stack(

  alignment: const Alignment(0.6, 0.6),

  children: [

    CircleAvatar(

      backgroundImage: NetworkImage(

        'https://avatars3.githubusercontent.com/u/14101776?v=4'),

    ),

    Container(

      decoration: BoxDecoration(

          color: Colors.black45,

      ),

      child: Text('Flutter'),

    ),

  ],

)

上面的示例代码使用 Stack 将一个 Container (将 Text 显示在一个半透明的黑色背景上) 覆盖在一个 CircleAvatar 上。Stack 使用对齐属性和 Alignment 坐标微调文本。  

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/14c3535ec33f1b7fdcaf8dbfbe96df7d)


Android   

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/3c6aa59ca63f89915f81607e0da77aca)


 iOS  

如果想了解更多相关信息,请参考 Stack 类文档。  

文档链接:https://api.flutter.dev/flutter/widgets/Stack-class.html  

七、风格化  

7.1 如何设置组件的风格?  

在 React Native 中,内联风格化和 stylesheets.create 可以用于设置组件的风格。  

// React Native

<View style={styles.container}>

  <Text style={{ fontSize: 32, color: 'cyan', fontWeight: '600' }}>

    This is a sample text

  

const styles = StyleSheet.create({

  container: {

    flex: 1,

    backgroundColor: '#fff',

    alignItems: 'center',

    justifyContent: 'center'

  }

});

在 Flutter 中, Text widget 可以接受 TextStyle 作为它的风格化属性。如果你想在不同的场合使用相同的文本风格,你可以创建一个 TextStyle 类,并且在多个 Text widget 中使用它。  

// Flutter

var textStyle = TextStyle(fontSize: 32.0, color: Colors.cyan, fontWeight:

   FontWeight.w600);

    ...

Center(

  child: Column(

    children: [

      Text(

        'Sample text',

        style: textStyle,

      ),

      Padding(

        padding: EdgeInsets.all(20.0),

        child: Icon(Icons.lightbulb_outline,

          size: 48.0, color: Colors.redAccent)

      ),

    ],

  ),

)

![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/opt/1/5a88d274309a1a2b3eede9d68b63f313)


Android  




iOS  

<figure class="Editable-styled" data-block="true" data-editor="b4g4h" data-offset-key="lml4-0-0" style="margin-top: 1.4em;margin-bottom: 1.4em;color: rgb(26, 26, 26);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif;font-size: 16px;text-align: start;white-space: pre-wrap;background-color: rgb(255, 255, 255);">


7.2 我如何使用 Icons 和 Colors 呢?


</figure>React Native 并不包含默认图标,所以需要使用第三方库。  

在 Flutter 中,引用 Material 库的时候就同时引入了 Material icons 和 colors。  

Icon(Icons.lightbulb_outline, color: Colors.redAccent)

当使用 Icons 类时,确保在项目的 pubspec.yaml 文件中设置 uses-material-design: true。这样保证 MaterialIcons 相关字体被包含在你的应用中。  

name: my_awesome_application

flutter: uses-material-design: true

Flutter 的 Cupertino (iOS-style) 包为 iOS 设计语言提供高分辨率的 widget。要使用 CupertinoIcons 字体,在项目的 pubspec.yaml 文件中添加 cupertino_icons 的依赖即可。  

name: my_awesome_application

dependencies:

  cupertino_icons: ^0.1.0

要在全局范围内自定义组件的颜色和风格,使用 ThemeData 为不同的主题指定默认颜色。在 MaterialApp 的主题属性中设置 ThemeData 对象。Colors 类提供 Material Design color palette 中所提供的颜色配置。  

下面的示例代码将主色调设置为 blue 然后文本颜色设置为 red。  

class SampleApp extends StatelessWidget {

  @override

 widget build(BuildContext context) {

    return MaterialApp(

      title: 'Sample App',

      theme: ThemeData(

        primarySwatch: Colors.blue,

        textSelectionColor: Colors.red

      ),

      home: SampleAppPage(),

    );

  }

}

7.3 如何增加风格化主题?  

在 React Native,常用主题都定义在 stylesheets 中。  

在 Flutter 中,为所有组件创建统一风格可以在 ThemeData 类中���义,并将它赋值给 MaterialApp 的主题属性。  

  @override

 widget build(BuildContext context) {

    return MaterialApp(

      theme: ThemeData(

        primaryColor: Colors.cyan,

        brightness: Brightness.dark,

      ),

      home: StylingPage(),

    );

  }

Theme 可以在不使用 MaterialApp widget 的情况下使用。Theme 接受一个 ThemeData 参数,并且将 ThemeData 应用于它的全部子 widget。  

 @override

 widget build(BuildContext context) {

    return Theme(

      data: ThemeData(

        primaryColor: Colors.cyan,

        brightness: brightness,

      ),

      child: Scaffold(

         backgroundColor: Theme.of(context).primaryColor,

              ...

              ...

      ),

    );

  }



## 



(未完待续)  
 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
如何有效定位Flutter内存问题? 4年以前  |  16302次阅读
鸿蒙系统起飞!Flutter 完全适配指南 2年以前  |  12697次阅读
Flutter的手势GestureDetector分析详解 5年以前  |  12411次阅读
Flutter插件详解及其发布插件 5年以前  |  12139次阅读
 目录