前语
这是笔者作为一个Android
工程师入门Flutter
的学习笔记,笔者不想经过一种循规蹈矩的方式来学习:先学Dart
语言,然后学习Flutter
的基本运用,再到实践运用这样的步骤。这样的方式有点无趣且效率较低。
笔者觉得对于已经有Android
根底的来说,经过类比Android
的方式来学习Flutter
,掌握核心根底概念后,直接开发实践运用,在这个进程中去学习其间的知识比如Dart
语法、深化的知识点。这是笔者的一次学习尝试,并将其记录下来:
给Android工程师的Flutter入门手册(一)
给Android工程师的Flutter入门手册(二)
本篇是该系列的第三篇,主要内容是:
(1)布局:Android常用的布局对应Flutter的完成
(2)列表视图和适配器:Flutter中怎么完成列表展示和适配
(3)主题:主题的运用
布局
LinearLayout
在 Android
中,LinearLayout
用于线性布局 widget
的——水平或许垂直。在 Flutter
中,运用 Row
或许 Column
Widget来完成相同的作用。
Widget getRowWidget() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
Widget getColumnWidget() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Column One'),
Text('Column Two'),
Text('Column Three'),
Text('Column Four'),
],
);
}
仔细看上面代码,会发现除了Row
和Column
widget 以外是一模一样的。它们的子级是一样的,这个特性能够被充分利用来开发包括有相同的子级,可是会随时刻改动的杂乱布局。
RelativeLayout
在Android中
,RelativeLayout
表明相对布局,经过 Widget
的相互方位对它们进行布局。
在 Flutter
中,能够经过组合运用 Column
、Row
和 Stack Widget
完成 RelativeLayout
的作用。
层叠布局 Stack
和 Web
中的绝对定位、Android
中的 Frame
布局是相似的,子组件能够依据距父容器四个角的方位来确认本身的方位。层叠布局答应子组件依照代码中声明的次序堆叠起来。
Flutter
中运用Stack
和Positioned
这两个组件来配合完成绝对定位。Stack
答应子组件堆叠,而Positioned
用于依据Stack
的四个角来确认子组件的方位。
Widget getRelativeLayoutWidget() {
// ConstrainedBox来保证Stack占满屏幕
return ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
children: <Widget>[
Container(
color: Colors.red,
child: const Text("Hello Flutter",style: TextStyle(color: Colors.white)),
),
const Positioned(
left: 18.0,
child: Text("Text1 On Left"),
),
const Positioned(
top: 18.0,
child: Text("Text2 On Top"),
),
const Positioned(
right: 18.0,
child: Text("Text3 On Right"),
),
const Positioned(
bottom: 18.0,
child: Text("Text3 On Right"),
)
],
),
);
}
经过Stack
和Positioned
,能够指定一个或多个子元素相对于父元素各个边的精确偏移,而且能够重叠。
但假如我们只想简略的调整一个子元素在父元素中的方位的话,运用Align
组件会更简略一些。
Align
组件能够调整子组件的方位:
Align({
Key key,
this.alignment = Alignment.center,
this.widthFactor,
this.heightFactor,
Widget child,
})
-
alignment
: 需求一个AlignmentGeometry
类型的值,表明子组件在父组件中的起始位 -
widthFactor
和heightFactor
是用于确认Align
组件本身宽高的属性;它们是两个缩放因子,会分别乘以子元素的宽、高,终究的结果就是Align
组件的宽高。假如值为null
,则组件的宽高将会占用尽可能多的空间。
ScrollView
在 Android
中,运用 ScrollView
布局 `widget,假如用户的设备屏幕比运用的内容区域小,用户能够滑动内容。
在 Flutter
中,完成这个功用的最简略的办法是运用 ListView
widget, Flutter 中 ListView
widget 既能够说是Android
中 ScrollView
,也是 ListView
最简略的用法就是这样:
Widget getScrollListView() {
return SizedBox(
width: 200,
height: 100,
child: Align(alignment: Alignment.center,
child: ListView(
scrollDirection: Axis.vertical,
children: const <Widget>[
Text('ListView One', ),
Text('ListView Two', ),
Text('ListView Three', ),
Text('ListView Four', ),
Text('ListView Five', ),
Text('ListView Six', ),
],
),
));
}
可是只用List是没有翻滚条,怎么快速加上呢:
Scrollbar
是一个Material风格的翻滚条,假如要给可翻滚组件增加翻滚条,只需将Scrollbar
作为可翻滚组件的恣意一个父级组件即可:
Widget getScrollListView() {
return Scrollbar(
child: SizedBox(
width: 200,
height: 100,
child: Align(
alignment: Alignment.center,
child: ListView(
scrollDirection: Axis.vertical,
children: const <Widget>[
Text('ListView One', ),
Text('ListView Two', ),
Text('ListView Three', ),
Text('ListView Four', ),
Text('ListView Five', ),
Text('ListView Six', ),
],
),
)));
}
列表视图和适配器
运用 Android
的 ListView
时,创立一个 adapter
并将其传给 ListView
, ListView
烘托 adapter
返回的每一行内容。然后,你需求保证回收了每一行视图,否则,你会遇到各种古怪的界面和内存问题。
由于 Flutter widget
不可变的特点,你需求向 ListView
传入一组 widget
,Flutter
会保证滑动的快速顺畅。
增加分割线和点击事件
完成一个带分割线,而且每个Item能够呼应点击的ListView:
Widget getListView() {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
debugPrint('item tapped $index');
},
child: ListTile(title: Text("ITEM $index")),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider(color: Colors.blue);
},
itemCount: 100);
}
怎么动态更新 ListView?
在 Android
中,经过adapter
调用notifyDataSetChanged
完成列表刷新。
在 Flutter
中,假如你预备在setState()
里更新一组 widget
,你很快会发现你的数据并没有更新到界面上。这是由于当setState()
被调用的时候, Flutter
烘托引擎会检查Widget
树是否有任何更改。当引擎检查到ListView
,他会履行==
检查,并判断两个ListView
是一样的。没有任何更改,所以也就不需求更新。
所以,更新ListView
的一个简略办法是,在setState()
里创立一个新的List
,并将数据从旧列表拷贝到新列表。尽管这个办法很简略,可是不推荐在大数据集的时候运用。
推荐的高效且有用的创立一个列表的办法是运用 ListView.Builder
。这个办法非常适用于动态列表或许具有很多数据的列表。能够理解它就是Android
里的 RecyclerView
,会为你自动回收列表项:
对上末节代码做个改造,完整代码如下:
class _SampleAppPageState extends State<SampleAppPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ListView Demo'),
),
body: getListView());
}
List<Widget> widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getItemView(i));
}
}
Widget getListView() {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
return getItemView(index);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider(color: Colors.blue);
},
itemCount: widgets.length);
}
Widget getItemView(int index) {
return GestureDetector(
onTap: () {
debugPrint('item tapped $index');
setState(() {
debugPrint('item setState $index');
widgets.add(getItemView(index + 1)); // tap后增加一个新数据
});
},
child: ListTile(title: Text("ITEM $index")),
);
}
}
其间ItemBuilder
办法和 Android adapter
里的getView
办法相似;它经过方位返回你希望在这个方位烘托的列表项。
最重要的一条是,onTap()
办法不重建列表项,而是对widget
调集履行元素增加的操作,增加后就会自动动态更新ListView
的数据显现了
主题
Android
中你在 XML
文件中定义主题并在 AndroidManifest.xml
中将其赋值给你的运用。
Flutter
中是在顶层 Widget
上声明主题。为了在运用中利用好 Material
组件,能够在运用中声明一个顶层 Widget
–MaterialApp
作为进口。
怎么定义主题
Flutter
供给开箱即用的美丽的 Material Design
完成,能够满足你通常需求的各种款式和主题的需求。MaterialApp
是一个包装了一系列 Widget
的为你给予便当的 Widget
,而这些 Widget
通常是完成 Material Design
的运用所必须的。它根据 WidgetsApp
并增加了Material
相关的功用。
当然能够运用WidgetApp
作为运用的 Widget
,它会供给一些相同的功用,可是不如MaterialApp
供给的功用丰厚。
假如要自定义恣意子组件的色彩或许款式,给MaterialApp
这个Widget
传入一个ThemeData
对象即可。
例如,鄙人面的代码中,主色调设置为蓝色,定义一些文本主题:
Widget build(BuildContext context) {
const appName = 'Custom Themes';
return MaterialApp(
title: appName,
theme: ThemeData(
primarySwatch: Colors.blue, // 主色调设置为蓝色
// 文本主题
textTheme: const TextTheme(
displayLarge: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
titleLarge: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
bodyMedium: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
),
),
home: const MyHomePage(
title: appName,
),
);
}
运用文本主题
定义好文本主题后,就能够运用到Text
`中:
body: Center(
child: Container(
color: Theme.of(context).colorScheme.secondary,
child: Text(
'This is a theme text',
style: Theme.of(context).textTheme.titleLarge,
),
),
)
参考
LinearLayout部分
How to design LinearLayout in Flutter.
BoxDecoration
Flutter编程之BoxDecoration用法详解
主题部分
运用 Themes 统一色彩和字体风格