📅  最后修改于: 2020-12-08 04:39:03             🧑  作者: Mango
由于Flutter的核心概念是“一切都是小部件” ,因此Flutter将用户界面布局功能合并到小部件本身中。 Flutter提供了许多专门设计的小部件,例如Container,Center,Align等,仅用于布局用户界面。通过组合其他窗口小部件来构建的窗口小部件通常使用布局窗口小部件。让我们在本章中学习Flutter布局概念。
布局小部件可以根据其子项分为两个不同的类别:
让我们在接下来的部分中学习小部件的类型及其功能。
在此类别中,小部件将只有一个小部件作为其子级,并且每个小部件都将具有特殊的布局功能。
例如,“中心”窗口小部件仅将其子窗口小部件相对于其父窗口小部件居中,而“容器”窗口小部件提供了完全的灵活性,可以使用填充,装饰等不同选项将其子窗口小部件放置在其内部的任何给定位置,
单个子窗口小部件是创建具有单个功能(例如按钮,标签等)的高质量窗口小部件的理想选择,
使用Container小部件创建简单按钮的代码如下-
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)
),
),
);
}
}
在这里,我们使用了两个小部件-容器小部件和文本小部件。小部件的结果是一个自定义按钮,如下所示-
让我们检查一下Flutter提供的一些最重要的单子布局小部件-
填充-用于通过给定的填充来排列其子小部件。在这里,可以由EdgeInsets类提供填充。
Align-使用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-缩放子窗口小部件,然后根据指定的适合度对其进行定位。
AspectRatio-尝试将子窗口小部件的大小调整为指定的宽高比
约束框
基准线
分形框
固有高度
内在宽度
限制框
舞台
溢出框
大小框
SizedOverflowBox
转变
CustomSingleChildLayout
我们的hello world应用程序正在使用基于材质的布局小部件来设计主页。让我们修改hello world应用程序,以使用下面指定的基本布局小部件来构建主页-
容器-具有对齐,填充,边框和边距以及丰富样式功能的通用,单个,基于框的容器小部件。
中心-简单的单个子容器小部件,将其子小部件居中。
MyHomePage和MyApp小部件的修改后的代码如下-
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,
),
)
);
}
}
这里,
容器小部件是顶层或根小部件。使用装饰和填充属性配置容器以布局其内容。
BoxDecoration具有许多属性,例如color,border等,用于装饰Container小部件,这里, color用于设置容器的颜色。
使用dgeInsets类设置Container小部件的padding ,该类提供了用于指定padding值的选项。
Center是Container小部件的子小部件。同样,“文本”是“中心”小部件的子级。文本用于显示消息,而Center用于使文本消息相对于父窗口小部件Container居中。
上面给出的代码的最终结果是一个布局示例,如下所示-
在此类别中,一个给定的窗口小部件将具有多个子窗口小部件,并且每个窗口小部件的布局都是唯一的。
例如,“行”小部件允许沿水平方向布置其子级,而“列”小部件允许沿垂直方向布置其子级。通过组成Row和Column ,可以构建具有任何复杂程度的小部件。
让我们学习本节中一些常用的小部件。
行-允许水平排列其子级。
列-允许垂直排列其子级。
ListView-允许将其子级排列为列表。
GridView-允许将其子级安排为图库。
扩展-用于使“行”和“列”小部件的子级占据最大可能面积。
表格-基于表格的小部件。
流-基于流的小部件。
堆栈-堆栈基于构件。
在本节中,让我们学习如何使用单个和多个子布局小部件创建具有自定义设计的产品列表的复杂用户界面。
为此,请遵循以下顺序-
在Android studio product_layout_app中创建一个新的Flutter应用程序。
用以下代码替换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小部件,然后删除了相关代码。
现在,根据指定的设计创建一个新的小部件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: [
Image.asset("assets/appimages/" +image), Expanded(
child: Container(
padding: EdgeInsets.all(5), child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(this.name, style: TextStyle(fontWeight:
FontWeight.bold)), Text(this.description),
Text("Price: " + this.price.toString()),
],
)
)
)
]
)
)
);
}
}
请遵守以下代码-
ProductBox使用了以下四个参数-
名称-产品名称
说明-产品说明
价格-产品价格
图片-产品图片
ProductBox使用七个内置小部件,如下所示-
ProductBox是使用上述小部件设计的。小部件的排列或层次结构在下图所示的图中指定-
现在,在应用程序的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小部件中的ProductBox小部件,如下所示-
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: [
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小部件的子级。
产品布局应用程序(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: [
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: [
Image.asset("assets/appimages/" + image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
this.name, style: TextStyle(
fontWeight: FontWeight.bold
)
),
Text(this.description), Text(
"Price: " + this.price.toString()
),
],
)
)
)
]
)
)
);
}
}
该应用程序的最终输出如下-