1.Flutter
1.Flutter是Google的UI工具包,用于从单个代码库构建漂亮的、本地编译的移动、web和桌面应用程序。
2.基本布局概念
欢迎来到Flutter布局代码实验室,在这里您可以学习如何在不下载和安装Flutter或Dart的情况下构建Flutter用户界面!
重要提示:这个代码实验室使用一个名为DartPad的实验性代码编辑器涵盖了基本的Fullter布局概念。DartPad还没有在所有浏览器上进行全面测试。如果在特定浏览器上使用DartPad时遇到任何困难,请创建一个DartPad问题,并在问题标题中指定要使用的浏览器。
Flutter不同于其他框架,因为它的UI是在代码中构建的,而不是(例如)在XML文件或类似文件中。Widget是Flutter用户界面的基本构建块。当你通过这个代码实验室,你会了解到几乎所有的Fullter都是一个Widget。Widget是一个不可变的对象,它描述了UI的特定部分。您还将了解Fullter Widget是可组合的,也就是说,可以组合现有的Widget来制作更复杂的Widget。在这个代码实验室的最后,您将能够将所学应用到构建一个显示名片的Flutter UI中。
估计完成此代码实验室的时间:45-60分钟。
Example: Creating a Column
test1.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
#########
Example: Modifying axis size
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max, //区别 增加了一个
children: [
BlueBox(),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Modifying main axis alignment
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start, //区别 修改为
children: [
BlueBox(),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Modifying cross axis alignment
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BlueBox(),
BiggerBlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
class BiggerBlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Changing fit properties
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
Flexible(
fit: FlexFit.loose,
flex: 1,
child: BlueBox(),
),
Flexible(
fit: FlexFit.loose,
flex: 1,
child: BlueBox(),
),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Testing flex values
在下面的示例中,行包含一个BooBox Widget和两个灵活的Widget,它们封装两个Box Box控件。灵活的Widget包含Flex属性,Flex值设置为1(默认值)。
当Flex属性彼此比较时,它们的挠曲值之间的比率决定每个柔性 Widget接收的总剩余空间的分数。
内容复制
剩余空间*(flex/所有flexvalue的总和)
在这个示例中,Flex值的总和(2)确定两个灵活的Widget接收总剩余空间的一半。BlueBox Widget(或固定大小的Widget)保持相同的大小。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
Flexible(
fit: FlexFit.tight,
flex: 1,
child: BlueBox(),
),
Flexible(
fit: FlexFit.tight,
flex: 1,
child: BlueBox(),
),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Filling extra space
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
###2. Wrap the second BlueBox
widget in an Expanded
widget.
Expanded(child: BlueBox(),),
Example: Resizing a widget
增加
Add a height
property equal to 100 logical pixels inside the SizedBox
widget, and run again.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max,
children: [
BlueBox(),
SizedBox(
width: 100,
child: BlueBox(),
),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Creating space
Create more space by adding another SizedBox
widget (25 logical pixels wide) between the second and third BlueBox
widgets, and run again.
通过在第二个和第三个BlueBox Widget之间添加另一个SizedBox Widget(25个逻辑像素宽)来创建更多空间,然后再次运行。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
SizedBox(width: 50),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Creating more space
Add another Spacer
widget (also with a flex
value of 1) between the second and third BlueBox
widgets.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
BlueBox(),
Spacer(flex: 1),
BlueBox(),
BlueBox(),
],
);
}
}
class BlueBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example: Aligning text
Change CrossAxisAlignment.center
to CrossAxisAlignment.baseline
, and run again.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
'Hey!',
style: TextStyle(
fontSize: 30,
fontFamily: 'Futura',
color: Colors.blue,
),
),
Text(
'Hey!',
style: TextStyle(
fontSize: 50,
fontFamily: 'Futura',
color: Colors.green,
),
),
Text(
'Hey!',
style: TextStyle(
fontSize: 40,
fontFamily: 'Futura',
color: Colors.red,
),
),
],
);
}
}
Example: Creating an Icon
Add another Icon
from the Material Icon library with a size of 50.
Give the Icon
a color of Colors.amber
from the Material Color palette,
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Icon(
Icons.widgets,
size: 50,
color: Colors.blue,
),
Icon(
Icons.widgets,
size: 50,
color: Colors.red,
),
],
);
}
}
Example: Displaying an image
- Change the short URL to the actual URL:
https://github.com/flutter/website/blob/master/examples/layout/sizing/images/pic3.jpg?raw=true
- Then change
pic3.jpg
topic1.jpg
orpic2.jpg
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network('https://urlzs.com/RsqCz'),
],
);
}
}
学习Widget
你将学到什么
如何回应轻拍。
如何创建自定义Widget。无状态和有状态Widget之间的区别。
如何修改应用程序使其对用户输入做出反应?在本教程中,您将向只包含非交互式Widget的应用程序添加交互性。具体地说,您将修改一个图标,通过创建一个自定义的有状态Widget来管理两个无状态Widget,使其可点击。
布局教程向您展示了如何为下面的屏幕快照创建布局。布局教程应用程序
布局教程应用程序
当这个应用程序第一次启动时,它的星星是红色的,这表明这个湖以前很受欢迎。星星旁边的数字表明有41个人喜欢这个湖。完成本教程后,轻敲星号将删除其收藏状态,用轮廓替换实心星号并减少计数。再次点击最喜欢的湖,画一个固体恒星和增加计数。
将创建的自定义Widget,为了实现这一点,您将创建一个单独的自定义Widget,其中包括star和count,它们本身就是Widget。点击两个Widget的星型改变状态,所以同一个Widget应该同时管理这两个。您可以在步骤2中直接接触代码:子类StatefulWidget。如果要尝试不同的状态管理方式,请跳到管理状态。
有状态和无状态Widget
Widget是有状态的或无状态的。如果Widget可以在用户与它交互时更改,例如它是有状态的。
无状态的Widget永远不会改变。
图标、图标按钮和文本是无状态Widget的示例。无状态Widget子类 无状态Widget。有状态的Widget是动态的:
例如,它可以根据用户交互触发的事件或在接收数据时更改其外观。Checkbox、Radio、Slider、InkWell、Form和TextField是有状态Widget的示例。StatefulWidget子类StatefulWidget。
Widget的状态存储在状态对象中,将Widget的状态与其外观分离。状态由可以更改的值组成,如滑块的当前值或是否选中复选框。当Widget的状态改变时,state对象调用setState(),告诉框架重新绘制Widget。
创建状态Widget,有什么意义?
有状态Widget由两个类实现:stateful widget的子类和State的子类。
state类包含Widget的可变状态和Widget的build()方法。当Widget的状态改变时,state对象调用setState(),告诉框架重新绘制Widget。
在本节中,您将创建一个自定义的有状态Widget。您将用一个单独的自定义有状态Widget来替换两个无状态Widget(实心红星和旁边的数字计数),该有状态Widget管理具有两个子部件(IconButton和Text)。
实现自定义状态Widget需要创建两个类:StatefulWidget的一个子类,用于定义Widget。State的一个子类,包含该Widget的状态并定义Widget的build()方法。本节向您展示如何为lakes应用程序构建一个名为FavoriteWidget的有状态Widget。设置之后,第一步是选择如何管理FavoriteWidget的状态。
第0步:准备
如果您已经在布局教程中构建了应用程序(步骤6),请跳到下一节。确保你已经设置好你的环境。
创建一个基本的“你好世界Fullter应用程序。
将lib/main.dart文件替换为main.dart。
将pubspec.yaml文件替换为pubspec.yaml。
在项目中创建一个images目录,并添加lake.jpg。
一旦你有了一个连接并启用的设备,或者你已经启动了iOS模拟器(Flutter安装的一部分),你就可以开始了!
步骤1:决定哪个对象管理Widget的状态
Widget的状态可以通过多种方式管理,但在我们的示例中,Widget本身FavoriteWidget将管理自己的状态。在本例中,切换star是一个独立的操作,不会影响父Widget或UI的其他部分,因此Widget可以在内部处理其状态。
在管理状态中,进一步了解Widget和状态的分离,以及如何管理状态。
第2步:StatefulWidget子类
FavoriteWidget类管理自己的状态,因此它重写createState()以创建状态对象。当框架想要构建Widget时,它会调用createState()。在本例中,createState()返回一个FavoriteWidgetState实例,您将在下一步中实现该实例。
lib/main.dart(FavoriteWidget)
内容复制
类FavoriteWidget扩展StatefulWidget{
@覆盖
_FavoriteWidgetState createState()=>FavoriteWidgetState();
}
注意:以下划线(\u)开头的成员或类是私有的。有关详细信息,请参见Dart语言教程中的库和可见性部分。
步骤3:子类状态
FavoriteWidgetState类存储可变数据
简单的应用程序状态管理
provider_shopper
为了说明这一点,请考虑以下简单的应用程序。
应用程序有两个独立的屏幕:目录和购物车(分别由MyCatalog和MyCart Widget表示)。它可能是一个购物应用程序,但你可以想象在一个简单的社交网络应用程序中有相同的结构(将catalog替换为wall,cart替换为favorites)。
目录屏幕包括一个自定义应用程序栏(MyAppBar)和许多列表项(MyListItems)的滚动视图。
这里的应用程序可视化为一个Widget树。
访问状态
一个简单的选项是提供一个回调,单击MyListItem时可以调用它。Dart的函数是一级对象,因此可以按任何方式传递它们。因此,在MyCatalog中可以有以下内容:
这可以正常工作,但是对于一个需要从许多不同地方修改的应用程序状态,您必须传递很多回调,这些回调很快就会变老。
幸运的是,Flutter有Widget向其后代提供数据和服务的机制(换句话说,不只是他们的孩子,而是他们下面的任何Widget)。正如您在Flutter中所期望的那样,在Flutter中,所有东西都是Widget™,这些机制只是特殊类型的widgets InheritedWidget、InheritedNotifier、InheritedModel等等。我们不会在这里报道这些,因为它们对于我们正在努力做的事情来说有点低级。
相反,我们将使用一个与低级Widget一起工作但简单易用的包。它被称为提供者。
使用provider,您不需要担心回调或继承的Widget。
但你需要明白3概念:
- 变更通知程序
- 更改通知提供程序
- 消费者
ChangeNotifier
changennotifier是flutrsdk中包含的一个简单类,它向监听器提供更改通知。换言之,如果某物是变更通知程序,则可以订阅其变更。(对于熟悉这个术语的人来说,这是一种可观察的形式。)
在provider中,ChangeNotifier是封装应用程序状态的一种方法。对于非常简单的应用程序,您只需使用一个ChangeNotifier。在复杂的模型中,您将有几个模型,因此有几个变更通知程序。(您根本不需要将ChangeNotifier与provider一起使用,但它是一个易于使用的类。)
在我们的购物应用程序示例中,我们希望在ChangeNotifier中管理购物车的状态。我们创建了一个扩展类,如下所示:
ChangeNotifierProvider
ChangeNotifierProvider是向其后代提供ChangeNotifier实例的Widget。它来自提供者包。
我们已经知道将ChangeNotifierProvider:放在需要访问它的Widget上方的位置。在CartModel的例子中,这意味着在MyCart和MyCatalog之上。
您不想将ChangeNotifierProvider放置得高于需要的位置(因为您不想污染作用域)。但在我们的例子中,MyCart和MyCatalog上唯一的Widget是MyApp。
void main() {
runApp(
ChangeNotifierProvider(
builder: (context) => CartModel(),
child: MyApp(),
),
);
}
Consumer
现在CartModel通过顶部的ChangeNotifierProvider声明提供给我们应用程序中的Widget,我们可以开始使用它了。
return Consumer<CartModel>( builder: (context, cart, child) { return Text("Total price: ${cart.totalPrice}"); }, );
我们必须指定要访问的模型的类型。在这种情况下,我们需要CartModel,所以我们编写Consumer<CartModel>。如果不指定泛型(<CartModel>),提供程序包将无法帮助您。
Consumer Widget唯一需要的参数是builder。Builder是一个函数,每当change通知程序更改时都会调用它。(换句话说,在模型中调用notifyListeners()时,将调用所有相应使用者小部件的所有生成器方法。)
调用生成器时有三个参数。context, cart, child 。
builder函数的第二个参数是ChangeNotifier的实例。这是我们最初的要求。您可以使用模型中的数据来定义UI在任何给定点的外观。
第三个参数是child,用于优化。如果您的使用者下面有一个大的widget子树,当模型改变时它不会改变,那么您可以构造它一次并通过构建器获得它。