- Flutter 教程
- Flutter - 首页
- Flutter - 简介
- Flutter - 安装
- 在 Android Studio 中创建简单的应用程序
- Flutter - 架构应用程序
- Dart 编程入门
- Flutter - Widget 入门
- Flutter - 布局入门
- Flutter - 手势入门
- Flutter - 状态管理
- Flutter - 动画
- Flutter - 编写 Android 特定代码
- Flutter - 编写 iOS 特定代码
- Flutter - 包入门
- Flutter - 访问 REST API
- Flutter - 数据库概念
- Flutter - 国际化
- Flutter - 测试
- Flutter - 部署
- Flutter - 开发工具
- Flutter - 编写高级应用程序
- Flutter - 总结
- Flutter 有用资源
- Flutter - 快速指南
- Flutter - 有用资源
- Flutter - 讨论
Flutter - 布局入门
由于 Flutter 的核心概念是“一切皆为 Widget”,因此 Flutter 将用户界面布局功能集成到 Widget 本身中。Flutter 提供了许多专门设计的 Widget,例如 Container、Center、Align 等,其唯一目的是布局用户界面。通过组合其他 Widget 构建的 Widget 通常使用布局 Widget。让我们在本节学习 Flutter 布局概念。
布局 Widget 的类型
根据其子 Widget,布局 Widget 可以分为两大类:
- 支持单个子 Widget 的 Widget
- 支持多个子 Widget 的 Widget
让我们在接下来的章节中学习这两种 Widget 及其功能。
单个子 Widget
在此类别中,Widget 只有一个子 Widget,并且每个 Widget 都有特殊的布局功能。
例如,Center Widget 只在其父 Widget 相对于其子 Widget 居中,而 Container Widget 通过使用填充、装饰等不同选项,提供完全的灵活性来将其子 Widget 放置在其内部的任何给定位置。
单个子 Widget 是创建具有单个功能(例如按钮、标签等)的高质量 Widget 的绝佳选择。
使用 Container Widget 创建简单按钮的代码如下:
class MyButton extends StatelessWidget { MyButton({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( border: Border( top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)), left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)), right: BorderSide(width: 1.0, color: Color(0xFFFF000000)), bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)), ), ), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0), decoration: const BoxDecoration( border: Border( top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)), left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)), right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)), bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)), ), color: Colors.grey, ), child: const Text( 'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black) ), ), ); } }
这里,我们使用了两个 Widget——一个 Container Widget 和一个 Text Widget。Widget 的结果是一个自定义按钮,如下所示:
让我们检查一下 Flutter 提供的一些最重要的单子 Widget:
Padding - 使用给定的填充来排列其子 Widget。此处,填充可以通过 EdgeInsets 类提供。
Align - 使用 alignment 属性的值在其内部对齐其子 Widget。alignment 属性的值可以通过 FractionalOffset 类提供。FractionalOffset 类以距左上角的距离来指定偏移量。
一些可能的偏移值如下:
FractionalOffset(1.0, 0.0) 表示右上角。
FractionalOffset(0.0, 1.0) 表示左下角。
关于偏移量的示例代码如下:
Center( child: Container( height: 100.0, width: 100.0, color: Colors.yellow, child: Align( alignment: FractionalOffset(0.2, 0.6), child: Container( height: 40.0, width: 40.0, color: Colors.red, ), ), ), )
FittedBox - 它缩放子 Widget,然后根据指定的 fit 进行定位。
AspectRatio - 它尝试将子 Widget 大小调整为指定的纵横比。
ConstrainedBox
Baseline
FractionalSizedBox
IntrinsicHeight
IntrinsicWidth
LimitedBox
OffStage
OverflowBox
SizedBox
SizedOverflowBox
Transform
CustomSingleChildLayout
我们的 Hello World 应用程序使用基于 Material 的布局 Widget 来设计主页。让我们修改我们的 Hello World 应用程序,使用如下所示的基本布局 Widget 来构建主页:
Container - 通用、单子、基于盒子的容器 Widget,具有对齐、填充、边框和边距以及丰富的样式功能。
Center - 简单、单子容器 Widget,用于使其子 Widget 居中。
MyHomePage 和 MyApp Widget 的修改后的代码如下:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MyHomePage(title: "Hello World demo app"); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration(color: Colors.white,), padding: EdgeInsets.all(25), child: Center( child:Text( 'Hello World', style: TextStyle( color: Colors.black, letterSpacing: 0.5, fontSize: 20, ), textDirection: TextDirection.ltr, ), ) ); } }
这里:
Container Widget 是顶级或根 Widget。Container 使用 decoration 和 padding 属性配置其内容布局。
BoxDecoration 具有许多属性,例如颜色、边框等,用于装饰 Container Widget,这里使用 color 设置容器的颜色。
Container Widget 的 padding 使用 EdgeInsets 类设置,该类提供指定填充值的选择。
Center 是 Container Widget 的子 Widget。同样,Text 是 Center Widget 的子 Widget。Text 用于显示消息,Center 用于相对于父 Widget Container 居中显示文本消息。
上面给出的代码的最终结果是一个布局示例,如下所示:
多个子 Widget
在此类别中,给定的 Widget 将有多个子 Widget,并且每个 Widget 的布局都是唯一的。
例如,Row Widget 允许将其子 Widget 水平排列,而 Column Widget 允许将其子 Widget 垂直排列。通过组合 Row 和 Column Widget,可以构建任何复杂程度的 Widget。
让我们在本节学习一些常用 Widget。
Row - 允许以水平方式排列其子 Widget。
Column - 允许以垂直方式排列其子 Widget。
ListView - 允许将其子 Widget 作为列表排列。
GridView - 允许将其子 Widget 作为图库排列。
Expanded - 用于使 Row 和 Column Widget 的子 Widget 占据尽可能大的区域。
Table - 基于表格的 Widget。
Flow - 基于流的 Widget。
Stack - 基于堆栈的 Widget。
高级布局应用程序
在本节中,让我们学习如何使用单子 Widget 和多子 Widget 创建具有自定义设计的复杂 产品列表 用户界面。
为此,请按照以下步骤操作:
在 Android Studio 中创建一个新的 Flutter 应用程序,product_layout_app。
将 main.dart 代码替换为以下代码:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue,), home: MyHomePage(title: 'Product layout demo home page'), ); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(this.title),), body: Center(child: Text( 'Hello World', )), ); } }
这里:
我们通过扩展 StatelessWidget 而不是默认的 StatefulWidget 来创建 MyHomePage Widget,然后删除了相关的代码。
现在,根据指定的如下所示的设计创建一个新的 Widget,ProductBox:
ProductBox 的代码如下。
class ProductBox extends StatelessWidget { ProductBox({Key key, this.name, this.description, this.price, this.image}) : super(key: key); final String name; final String description; final int price; final String image; Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(2), height: 120, child: Card( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Image.asset("assets/appimages/" +image), Expanded( child: Container( padding: EdgeInsets.all(5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text(this.name, style: TextStyle(fontWeight: FontWeight.bold)), Text(this.description), Text("Price: " + this.price.toString()), ], ) ) ) ] ) ) ); } }
请注意代码中的以下内容:
ProductBox 使用四个参数,如下所示:
name - 产品名称
description - 产品描述
price - 产品价格
image - 产品图片
ProductBox 使用七个内置 Widget,如下所示:
- Container
- Expanded
- Row
- Column
- Card
- Text
- Image
ProductBox 使用上述 Widget 设计。Widget 的排列或层次结构在如下所示的图中指定:
现在,将一些虚拟产品信息图片(见下文)放在应用程序的 assets 文件夹中,并在 pubspec.yaml 文件中配置 assets 文件夹,如下所示:
assets: - assets/appimages/floppy.png - assets/appimages/iphone.png - assets/appimages/laptop.png - assets/appimages/pendrive.png - assets/appimages/pixel.png - assets/appimages/tablet.png
iPhone.png
Pixel.png
Laptop.png
Tablet.png
Pendrive.png
Floppy.png
最后,在 MyHomePage Widget 中使用 ProductBox Widget,如下所示:
class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title:Text("Product Listing")), body: ListView( shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), children: <Widget> [ ProductBox( name: "iPhone", description: "iPhone is the stylist phone ever", price: 1000, image: "iphone.png" ), ProductBox( name: "Pixel", description: "Pixel is the most featureful phone ever", price: 800, image: "pixel.png" ), ProductBox( name: "Laptop", description: "Laptop is most productive development tool", price: 2000, image: "laptop.png" ), ProductBox( name: "Tablet", description: "Tablet is the most useful device ever for meeting", price: 1500, image: "tablet.png" ), ProductBox( name: "Pendrive", description: "Pendrive is useful storage medium", price: 100, image: "pendrive.png" ), ProductBox( name: "Floppy Drive", description: "Floppy drive is useful rescue storage medium", price: 20, image: "floppy.png" ), ], ) ); } }
这里,我们使用 ProductBox 作为 ListView Widget 的子 Widget。
产品布局应用程序 (product_layout_app) 的完整代码 (main.dart) 如下所示:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Product layout demo home page'), ); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Product Listing")), body: ListView( shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), children: <Widget>[ ProductBox( name: "iPhone", description: "iPhone is the stylist phone ever", price: 1000, image: "iphone.png" ), ProductBox( name: "Pixel", description: "Pixel is the most featureful phone ever", price: 800, image: "pixel.png" ), ProductBox( name: "Laptop", description: "Laptop is most productive development tool", price: 2000, image: "laptop.png" ), ProductBox( name: "Tablet", description: "Tablet is the most useful device ever for meeting", price: 1500, image: "tablet.png" ), ProductBox( name: "Pendrive", description: "Pendrive is useful storage medium", price: 100, image: "pendrive.png" ), ProductBox( name: "Floppy Drive", description: "Floppy drive is useful rescue storage medium", price: 20, image: "floppy.png" ), ], ) ); } } class ProductBox extends StatelessWidget { ProductBox({Key key, this.name, this.description, this.price, this.image}) : super(key: key); final String name; final String description; final int price; final String image; Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(2), height: 120, child: Card( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Image.asset("assets/appimages/" + image), Expanded( child: Container( padding: EdgeInsets.all(5), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text( this.name, style: TextStyle( fontWeight: FontWeight.bold ) ), Text(this.description), Text( "Price: " + this.price.toString() ), ], ) ) ) ] ) ) ); } }
应用程序的最终输出如下所示: