移动端混合开发Flutter入坑widgets介绍
Posted IT科技互联
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端混合开发Flutter入坑widgets介绍相关的知识,希望对你有一定的参考价值。
widgets介绍
编写“Hello World”
基础
Widget
使用
Materia
组件手势处理
Widget
状态响应处理购物车综合例子
一、编写“Hello World”
最小的 Flutter
应用程序只需调用 runApp()
函数使用 widget
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
runApp()
函数返回 Widget
并使其成为 Widget
树的root父级。 在此示例中, Widget
树由两个 Widget
小部件组成,即 CenterWidget
小部件及其子窗口 TextWidget
小部件。 框架强制root父级 widget
覆盖屏幕,这意味着Text Widget “Hello,world”最终以屏幕为中心。 在此实例中需要指定文本方向 textDirection
; 当使用 MaterialAppwidget
时,这将由你负责。
二、基础widgets
Flutter附带了一套功能强大的基础widget,其中以下是非常常用的widget:
Text: 创建一系列样式文本。
Row, 水平(行)和垂直(列)方向上创建灵活的布局。 它的设计基于web的flexbox布局模型。
Stack: Stack widget不是线性定向(水平或垂直),而是允许您按照绘制顺序将widget堆叠在一起。 然后,您可以在堆栈的子项上使用Positioned(坐标) widget,以相对于堆栈的顶部,右侧,底部或左侧边缘定位它们。 堆栈基于Web的绝对定位布局模型。
Container: Container widget允许您创建矩形可视元素。 容器可以使用BoxDecoration进行装饰,例如背景,边框或阴影。 Container也可以应用其大小的边距,填充和约束。 另外,可以使用矩阵在三维空间中变换容器。
下面是组合多种widget的例子:
import 'package:flutter/material.dart';
class MyAppBar extends StatelessWidget {
MyAppBar({this.title});
// Fields in a Widget subclass are always marked "final".
final Widget title;
@override
Widget build(BuildContext context) {
return Container(
height: 88.0, // in logical pixels
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 44.0),
decoration: BoxDecoration(color: Colors.blue[500]),
// Row is a horizontal, linear layout.
child: Row(
// <Widget> is the type of items in the list.
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
// Expanded expands its child to fill the available space.
Expanded(
child: title,
),
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
);
}
}
class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Material is a conceptual piece of paper on which the UI appears.
return Material(
// Column is a vertical, linear layout.
child: Column(
children: <Widget>[
MyAppBar(
title: Text(
'Example title',
style: Theme.of(context).primaryTextTheme.title,
),
),
Expanded(
child: Center(
child: Text('Hello, world!'),
),
),
],
),
);
}
}
void main() {
runApp(MaterialApp(
title: 'My app', // used by the OS task switcher
home: MyScaffold(),
));
}
三、使用Material组件
使用Material组件可以快速搭建app页面架构, Material应用程序以MaterialApp为root 父级widget,该widget在应用程序的根目录下构建了许多有用的widget,包括Navigator,它管理“路由”。 Navigator可让您在应用程序的屏幕之间平滑过渡。 使用MaterialApp Widget是一个构建app页面的好方法。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter Tutorial',
home: TutorialHome(),
));
}
class TutorialHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
title: Text('Example title'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
// body is the majority of the screen.
body: Center(
child: Text('Hello, world!'),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: Icon(Icons.add),
onPressed: null,
),
);
}
}
四、手势处理
基于上一段代码自定义一个带相应效果的button:
class MyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print('MyButton was tapped!');
},
child: Container(
height: 36.0,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Colors.lightGreen[500],
),
child: Center(
child: Text('Engage'),
),
),
);
}
}
GestureDetectorWidget
没有可视化,是是检测用户做出的手势。 当用户点击 Container
时, GestureDetector
会调用其 onTap
回调,在这种情况下会向控制台打印一条消息。 您可以使用 GestureDetector
检测各种输入手势,包括点击,拖动和缩放。
许多 Widget
使用 GestureDetector
为其他Widget提供可选的回调。 例如, IconButton
, RaisedButton
和 FloatingActionButtonWidget
具有 onPressed
回调,当用户点击小部件时会触发这些回调。
五、widget状态响应处理
到目前为止,我们只使用了 StatelessWidget
。 StatelessWidget
从其父 widget
接收参数,它们存储在成员变量中。 当要求构建 widget
时,它使用这些存储的值为它创建的widget派生新的参数。
为了构建更复杂的 widget
例如:
以更有趣的方式对用户输入做出反应
应用程序通常带有一些状态。
Flutter
使用 StatefulWidgets
实现这个方法。 StatefulWidgets
是特殊的 Widget
,它们知道如何生成 State
对象,然后用于保存状态。 考虑下面基本的例子:
class Counter extends StatefulWidget {
// This class is the configuration for the state. It holds the
// values (in this case nothing) provided by the parent and used by the build
// method of the State. Fields in a Widget subclass are always marked "final".
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
// This call to setState tells the Flutter framework that
// something has changed in this State, which causes it to rerun
// the build method below so that the display can reflect the
// updated values. If we changed _counter without calling
// setState(), then the build method would not be called again,
// and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance
// as done by the _increment method above.
// The Flutter framework has been optimized to make rerunning
// build methods fast, so that you can just rebuild anything that
// needs updating rather than having to individually change
// instances of widgets.
return Row(
children: <Widget>[
RaisedButton(
onPressed: _increment,
child: Text('Increment'),
),
Text('Count: $_counter'),
],
);
}
}
可能你们会疑惑为什么 StatefulWidget
和 State
是单独的声明。 在 Flutter
中,这两种类型的对象具有不同的生命周期。 Widget
是临时对象,用于构造应用程序当前状态的表示。 另一方面,状态对象在调用 build()
之间是持久的,允许它们缓存信息。
以下稍微复杂的示例显示了它在实践中的工作原理:
class CounterDisplay extends StatelessWidget {
CounterDisplay({this.count});
final int count;
@override
Widget build(BuildContext context) {
return Text('Count: $count');
}
}
class CounterIncrementor extends StatelessWidget {
CounterIncrementor({this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: onPressed,
child: Text('Increment'),
);
}
}
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return Row(children: <Widget>[
CounterIncrementor(onPressed: _increment),
CounterDisplay(count: _counter),
]);
}
}
注意我们是如何创建两个新的 StatelessWidget
,干净地分离 显示计数器(CounterDisplay)
和 更改计数器(CounterIncrementor
)的问题。 尽管最终结果与前面的示例相同,但是责任分离允许将更大的复杂性封装在各个 widget
中,同时保持父级的简单性。
六、购物车综合例子
以下是一个更完整的购物车示例,汇集了上面介绍的知识点, 显示出售的各种产品,并维护购物车的库存。
首先定义表示类ShoppingListItem:
class Product {
const Product({this.name});
final String name;
}
typedef void CartChangedCallback(Product product, bool inCart);
class ShoppingListItem extends StatelessWidget {
ShoppingListItem({Product product, this.inCart, this.onCartChanged})
: product = product,
super(key: ObjectKey(product));
final Product product;
final bool inCart;
final CartChangedCallback onCartChanged;
Color _getColor(BuildContext context) {
// The theme depends on the BuildContext because different parts of the tree
// can have different themes. The BuildContext indicates where the build is
// taking place and therefore which theme to use.
return inCart ? Colors.black54 : Theme.of(context).primaryColor;
}
TextStyle _getTextStyle(BuildContext context) {
if (!inCart) return null;
return TextStyle(
color: Colors.black54,
decoration: TextDecoration.lineThrough,
);
}
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onCartChanged(product, !inCart);
},
leading: CircleAvatar(
backgroundColor: _getColor(context),
child: Text(product.name[0]),
),
title: Text(product.name, style: _getTextStyle(context)),
);
}
}
ShoppingListItemWidget
遵循 StatelessWidget
的通用模式。 它将它在构造函数中接收的值存储在最终成员变量中,然后在构建函数中使用它们。 例如, inCart
bool值在两个视觉外观之间切换:一个使用当前主题的主要颜色,另一个使用灰色。
当用户点击列表item时, Widget
不会直接修改其 inCart
值。 相反, Widget
调用从其父 Wdiget
接收的 onCartChanged
函数。 此模式允许您在 Widget
层次结构中存储更高的状态,这会导致状态持续更长时间。 在极端情况下,传递给 runApp()
的 Widget
上存储的状态在应用程序的生命周期内持续存在。
当父级接收到onCartChanged回调时,父级会更新其内部状态,从而触发父级重建并使用新的 inCart
值创建 ShoppingListItem
的新实例。 尽管父级在重建时会创建 ShoppingListItem
的新实例,但该操作低耗,因为该框架将新构建的 Widget
与先前构建的 Widget
进行比较,并仅将差异应用于基础 RenderObject
。
class ShoppingList extends StatefulWidget {
ShoppingList({Key key, this.products}) : super(key: key);
final List<Product> products;
// The framework calls createState the first time a widget appears at a given
// location in the tree. If the parent rebuilds and uses the same type of
// widget (with the same key), the framework re-uses the State object
// instead of creating a new State object.
@override
_ShoppingListState createState() => _ShoppingListState();
}
class _ShoppingListState extends State<ShoppingList> {
Set<Product> _shoppingCart = Set<Product>();
void _handleCartChanged(Product product, bool inCart) {
setState(() {
// When a user changes what's in the cart, we need to change _shoppingCart
// inside a setState call to trigger a rebuild. The framework then calls
// build, below, which updates the visual appearance of the app.
if (inCart)
_shoppingCart.add(product);
else
_shoppingCart.remove(product);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Shopping List'),
),
body: ListView(
padding: EdgeInsets.symmetric(vertical: 8.0),
children: widget.products.map((Product product) {
return ShoppingListItem(
product: product,
inCart: _shoppingCart.contains(product),
onCartChanged: _handleCartChanged,
);
}).toList(),
),
);
}
}
void main() {
runApp(MaterialApp(
title: 'Shopping App',
home: ShoppingList(
products: <Product>[
Product(name: 'Eggs'),
Product(name: 'Flour'),
Product(name: 'Chocolate chips'),
],
),
));
}
ShoppingList
类继承了 StatefulWidget
,这意味着此 Widget
存储可变状态。 当 ShoppingListWidget
首次插入树中时,框架会调用 createState
函数来创建 _ShoppingListState
的新实例,以与树中的该位置相关联。 (请注意,State的子类通常以前导下划线命名,表示它们是私有实现细节。)当此 Widget
的父窗口重建时,父窗口创建 ShoppingList
的新实例,但框架重用已在树中的 _ShoppingListState
实例 而不是再次调用 createState
。
欢迎关注我的公众号,了解更多技术博文。
以上是关于移动端混合开发Flutter入坑widgets介绍的主要内容,如果未能解决你的问题,请参考以下文章
即将开源 | 让Flutter真正支持View级别的混合开发