【开源鸿蒙跨平台开发学习笔记 】DAY07 页面框架搭建以及相关组件详解
/ Expanded 让子组件填充可用空间Row(Expanded( // 这个 Container 会占据剩余的所有宽度),],Wrap(Chip(label: Text('标签1')),Chip(label: Text('标签2')),Chip(label: Text('很长的标签3')),Chip(label: Text('标签4')),Chip(label: Text('标签5')),//
虽然这几天也在不断的学习flutter相关的知识,但是并没有输出,学的有点凌乱不知道从何下手。今天加班不怎么忙,理了下思路:增加Gitcode 口袋功能,然后详解相关组件。
一、页面框架搭建

1、Scaffold
Scaffold 是 Flutter Material 组件库提供的一个路由页的骨架,很容易实现一个页面完整的页面,
Scaffold 的 bottomNavigationBar属性来设置底部导航
如上述截图代码:
NavigationBar buildNormalNavigationBar() {
return NavigationBar(
selectedIndex: currentIndex,
onDestinationSelected: (int index) {
setState(() {
currentIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
NavigationDestination(
icon: Icon(Icons.search_outlined),
selectedIcon: Icon(Icons.search),
label: '搜索',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: '我的',
),
],
);
}
2、Scaffold 的 AppBar 是一个 Material 风格的导航栏

appBar: AppBar(
title: Text('GitCode 口袋工具'),
leading: Builder(builder: (context){
return IconButton(
icon: Icon(Icons.settings,color: Colors.black54,size: 32,),
onPressed:(){
Scaffold.of(context).openDrawer();
},
);
}),
),
3、Scaffold 的 Drawer 实现抽屉样式

drawer: SettingDrawer(),
class SettingDrawer extends StatelessWidget{
const SettingDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: MediaQuery.removePadding(
context: context,
//移除抽屉菜单顶部默认留白
removeTop: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 38.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ClipOval(
child: Image.asset(
"images/avatar.jpg",
width: 80,
),
),
),
Text(
"夏小鱼",
style: TextStyle(fontWeight: FontWeight.bold),
)
],
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: const Icon(Icons.dark_mode),
title: const Text('深色模式'),
),
ListTile(
leading: const Icon(Icons.clear),
title: const Text('清楚缓存'),
),
ListTile(
leading: const Icon(Icons.info),
title: const Text('版本信息'),
),
],
),
),
],
),
),
);
}
4、Scaffold 的 FloatingActionButton可以实现导航栏底部挖空的效果

bottomNavigationBar: BottomAppBar(
color: Colors.white,
shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
notchMargin: 8,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
children: [
IconButton(icon: Icon(Icons.home), onPressed: () {}),
SizedBox(), //中间位置空出
IconButton(icon: Icon(Icons.person), onPressed: () {}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
shape: CircleBorder(),
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.green[300],
child: Icon(Icons.add, size: 40, color: Colors.white),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
二、首页

这个页面主要是信息展示,涉及到的是Text相关属性
import 'package:flutter/material.dart';
// 首页
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final mutedColor = theme.colorScheme.onSurfaceVariant;
return Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题和图标
Center(
child: Column(
children: [
const SizedBox(height: 32),
Text(
'GitCode 口袋工具',
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
const SizedBox(height: 8),
Text(
'方便快捷的 GitCode 助手',
style: theme.textTheme.bodyLarge?.copyWith(
color: mutedColor,
),
),
],
),
),
const SizedBox(height: 40),
// 项目介绍
_buildSection(
context,
title: '项目介绍',
icon: Icons.info_outline,
child: Text(
'GitCode 口袋工具 - 面向 OpenHarmony 生态的 Flutter 跨平台实践',
style: theme.textTheme.bodyMedium?.copyWith(height: 1.6),
),
),
const SizedBox(height: 24),
// 技术栈
_buildSection(
context,
title: '技术栈',
icon: Icons.build_outlined,
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
_buildTechChip(context, 'Flutter'),
_buildTechChip(context, 'Dio'),
_buildTechChip(context, 'Axios'),
_buildTechChip(context, 'Material Design 3'),
],
),
),
const SizedBox(height: 24),
// 使用提示
_buildSection(
context,
title: '使用提示',
icon: Icons.lightbulb_outline,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTipItem(context, '1. 在"搜索"页面输入关键字和 Access Token'),
const SizedBox(height: 8),
_buildTipItem(context, '2. 选择搜索模式:用户或仓库'),
const SizedBox(height: 8),
_buildTipItem(context, '3. 点击"查询"按钮开始搜索'),
const SizedBox(height: 8),
_buildTipItem(context, '4. 点击"查看全部"可进入分页列表页面'),
],
),
),
const SizedBox(height: 40),
// 版本信息
Center(
child: Text(
'版本 1.0.0',
style: theme.textTheme.bodySmall?.copyWith(
color: mutedColor,
),
),
),
],
),
),
);
}
Widget _buildSection(
BuildContext context, {
required String title,
required IconData icon,
required Widget child,
}) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: theme.colorScheme.primary, size: 24),
const SizedBox(width: 8),
Text(
title,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
child,
],
);
}
Widget _buildTechChip(BuildContext context, String label) {
return Chip(
label: Text(label),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
);
}
Widget _buildTipItem(BuildContext context, String text) {
final theme = Theme.of(context);
final muted = theme.colorScheme.onSurfaceVariant;
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 8),
Expanded(
child: Text(
text,
style: theme.textTheme.bodyMedium?.copyWith(
color: muted,
height: 1.5,
),
),
),
],
);
}
}
1、位置与对齐组件:Center
是对齐与相对定位(Align)其中的一种
Align({
Key key,
this.alignment = Alignment.center,
this.widthFactor,
this.heightFactor,
Widget child,
})
Align组件可以随意调整子组件的位置
alignment : 需要一个AlignmentGeometry类型的值,表示子组件在父组件中的起始位置。AlignmentGeometry 是一个抽象类,它有两个常用的子类:Alignment和 FractionalOffset,我们将在下面的示例中详细介绍。
widthFactor和heightFactor是用于确定Align 组件本身宽高的属性;它们是两个缩放因子,会分别乘以子元素的宽、高,最终的结果就是Align 组件的宽高。如果值为null,则组件的宽高将会占用尽可能多的空间。
Align(
widthFactor: 2,
heightFactor: 2,
alignment: Alignment.topRight,
child: Icon(Icons.rocket_launch,size: 96,color: Colors.green,),
);
Icon 的大小是96,widthFactor,heightFactor都是2,Align组件的大小就是 96 x 2
Icon 位于 Align 的右上角,但是 Alignment.topRight 是什么呢
static const Alignment topRight = Alignment(1.0, -1.0);
Alignment继承自AlignmentGeometry,表示矩形内的一个点,他有两个属性x、y,分别表示在水平和垂直方向的偏移

接下来就是 Center
class Center extends Align {
/// Creates a widget that centers its child.
const Center({super.key, super.widthFactor, super.heightFactor, super.child});
}
Center 继承自 Align,比 Align 只少了一个 alignment 参数;由于Align的构造函数中alignment 值为Alignment.center,因此可以认为 Center 组件其实是对齐方式确定(Alignment.center)了的Align
2、布局组件: Column
与 Row 一样都是线性布局(沿水平或垂直方向排列子组件)
对于线性布局,有主轴和纵轴之分,如果布局是沿水平方向,那么主轴就是指水平方向,而纵轴即垂直方向;如果布局沿垂直方向,那么主轴就是指垂直方向,而纵轴就是水平方向
在线性布局中,有两个定义对齐方式的枚举类MainAxisAlignment和CrossAxisAlignment,分别代表主轴对齐和纵轴对齐。
3、固定尺寸组件:SizedBox
是 Flutter 中一个非常常用的布局组件,用于控制子组件的尺寸或创建固定大小的空白空间
1) 固定尺寸的 SizedBox
SizedBox(
width: 100, // 宽度 100
height: 50, // 高度 50
child: Text('固定尺寸'), // 子组件
)
2)仅设置宽度或高度
SizedBox(
width: 100, // 宽度 100
height: 50, // 高度 50
child: Text('固定尺寸'), // 子组件
)
3)其它构造函数
SizedBox.shrink() // 创建一个尺寸为 0×0 的盒子
SizedBox.expand(
child: Text('填满父容器'), // 宽度和高度都充满父容器
)
SizedBox.fromSize(
size: Size(150, 80), // 使用 Size 对象指定尺寸
child: Text('通过 Size 设置'),
)
const SizedBox(width: 20, height: 20) // 使用 const 优化性能
4)实际场景
// 1、创建间距
Column(
children: [
Text('第一行'),
SizedBox(height: 20), // 创建 20px 的垂直间距
Text('第二行'),
SizedBox(height: 30), // 创建 30px 的垂直间距
Text('第三行'),
],
)
// 2、水平间距
Row(
children: [
Text('左边'),
SizedBox(width: 15), // 创建 15px 的水平间距
Text('右边'),
],
)
// 3、控制组件大小
// 限制按钮大小
SizedBox(
width: 200,
height: 60,
child: ElevatedButton(
onPressed: () {},
child: Text('固定大小的按钮'),
),
)
// 限制图片大小
SizedBox(
width: 120,
height: 120,
child: Image.asset('assets/logo.png'),
)
4、文本组件:Text
Text('基于 Flutter 的跨平台 GitCode 口袋工具,全面兼容 OpenHarmony 系统',
textAlign: TextAlign.left, // 文本的对齐方式;可以选择左对齐、右对齐还是居中
maxLines: 2,
overflow: TextOverflow.ellipsis,
textScaler: TextScaler.linear(1.5), // 可缩放1.5倍
style: TextStyle( //TextStyle用于指定文本显示的样式如颜色、字体、粗细、背景等
fontSize: 16,
color: Colors.green,
fontWeight: FontWeight.bold,
fontFamily: "Courier", // 字体
background: Paint()..color = Colors.yellow,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.dashed)),

1)textAlign
文本的对齐方式;可以选择左对齐、右对齐还是居中
2)maxLines、overflow
指定文本显示的最大行数,默认情况下,文本是自动折行的,如果指定此参数,则文本最多不会超过指定的行。如果有多余的文本,可以通过overflow来指定截断方式,默认是直接截断,本例中指定的截断方式TextOverflow.ellipsis,它会将多余文本截断后以省略符“...”表示
3)textScaleFactor
代表文本相对于当前字体大小的缩放因子,相对于去设置文本的样式style属性的fontSize,它是调整字体大小的一个快捷方式。该属性的默认值可以通过MediaQueryData.textScaleFactor获得,如果没有MediaQuery,那么会默认值将为1.0。
4)TextStyle
用于指定文本显示的样式如颜色、字体、粗细、背景等
5、Expanded
Expanded 是 Flutter 中用于弹性布局的重要组件,它让子组件在 Row、Column 或 Flex 中占据剩余空间。下面详细解析它的用法:
1)、什么是 Expanded?
// Expanded 让子组件填充可用空间
Row(
children: [
Container(width: 50, color: Colors.red),
Expanded( // 这个 Container 会占据剩余的所有宽度
child: Container(color: Colors.blue),
),
],
)
2)、级别语法
Expanded(
flex: 1, // 弹性系数(可选,默认为1)
child: YourWidget(), // 要扩展的子组件
)
3)、在Row中等分空间
Row(
children: [
Expanded( // 每个 Expanded 占据相同空间
child: Container(color: Colors.red, height: 50),
),
Expanded(
child: Container(color: Colors.green, height: 50),
),
Expanded(
child: Container(color: Colors.blue, height: 50),
),
],
)
4)、不同比例的空间分配
Row(
children: [
Expanded(
flex: 1, // 占据 1/6 的空间
child: Container(color: Colors.red, height: 50),
),
Expanded(
flex: 2, // 占据 2/6 的空间
child: Container(color: Colors.green, height: 50),
),
Expanded(
flex: 3, // 占据 3/6 的空间
child: Container(color: Colors.blue, height: 50),
),
],
)
5)、固定尺寸与弹性尺寸结合
Row(
children: [
Container(
width: 80, // 固定宽度
color: Colors.red,
height: 50,
),
Expanded( // 占据剩余宽度
child: Container(color: Colors.blue, height: 50),
),
Container(
width: 60, // 固定宽度
color: Colors.green,
height: 50,
),
],
)
6)、在Column中垂直等分
Column(
children: [
Expanded(
child: Container(color: Colors.red),
),
Expanded(
child: Container(color: Colors.green),
),
Expanded(
child: Container(color: Colors.blue),
),
],
)
7)、复杂垂直布局
Column(
children: [
Container(
height: 60, // 固定高度 - AppBar
color: Colors.grey,
child: Center(child: Text('标题栏')),
),
Expanded( // 占据剩余空间 - 主要内容
flex: 3,
child: Container(color: Colors.blue),
),
Expanded( // 占据剩余空间 - 次要内容
flex: 1,
child: Container(color: Colors.green),
),
Container(
height: 50, // 固定高度 - 底部栏
color: Colors.orange,
child: Center(child: Text('底部栏')),
),
],
)
8)、flex的计算规则
Row(
children: [
Expanded(
flex: 1, // 占据 1/(1+2+3) = 1/6 的空间
child: Container(color: Colors.red),
),
Expanded(
flex: 2, // 占据 2/(1+2+3) = 2/6 的空间
child: Container(color: Colors.green),
),
Expanded(
flex: 3, // 占据 3/(1+2+3) = 3/6 的空间
child: Container(color: Colors.blue),
),
],
)
9)、混合使用不同的flex值
Row(
children: [
Container(width: 50, color: Colors.grey), // 固定 50px
Expanded(
flex: 1, // 占据剩余空间的 1/4
child: Container(color: Colors.red),
),
Expanded(
flex: 3, // 占据剩余空间的 3/4
child: Container(color: Colors.blue),
),
],
)
6、Wrap
Wrap 是 Flutter 中一个灵活的布局组件,当子组件在主轴方向上空间不足时,它会自动换行到交叉轴方向。
1)、什么是Wrap
Wrap(
children: [
Chip(label: Text('标签1')),
Chip(label: Text('标签2')),
Chip(label: Text('很长的标签3')),
Chip(label: Text('标签4')),
Chip(label: Text('标签5')),
// 当一行放不下时自动换行
],
)
2)、基本语法
Wrap(
direction: Axis.horizontal, // 排列方向
alignment: WrapAlignment.start, // 主轴对齐方式
spacing: 8.0, // 主轴间距
runAlignment: WrapAlignment.start, // 交叉轴对齐方式
runSpacing: 8.0, // 交叉轴间距
children: [/* 子组件 */],
)
3)、排列方向
// 水平排列(默认)
Wrap(
direction: Axis.horizontal,
children: List.generate(10, (index) => Chip(label: Text('标签$index'))),
)
// 垂直排列
Wrap(
direction: Axis.vertical,
children: List.generate(10, (index) => Chip(label: Text('标签$index'))),
)
4)、alignment - 主轴对齐方式
Row(
children: [
_buildWrapExample('start', WrapAlignment.start),
_buildWrapExample('center', WrapAlignment.center),
_buildWrapExample('end', WrapAlignment.end),
_buildWrapExample('spaceBetween', WrapAlignment.spaceBetween),
_buildWrapExample('spaceAround', WrapAlignment.spaceAround),
_buildWrapExample('spaceEvenly', WrapAlignment.spaceEvenly),
],
)
Widget _buildWrapExample(String title, WrapAlignment alignment) {
return Expanded(
child: Column(
children: [
Text(title),
Container(
height: 100,
child: Wrap(
alignment: alignment,
children: List.generate(4, (index) => Chip(label: Text('$index'))),
),
),
],
),
);
}
5)、spacing - 主轴间距
Wrap(
spacing: 20.0, // 水平间距 20px
children: List.generate(6, (index) => Chip(label: Text('标签$index'))),
)
6)、runAlignment - 交叉轴对齐方式
Container(
height: 150,
color: Colors.grey[200],
child: Wrap(
runAlignment: WrapAlignment.spaceBetween, // 行与行之间的对齐
children: List.generate(8, (index) => Chip(label: Text('行$index'))),
),
)
7)、runSpacing - 交叉轴间距
Wrap(
runSpacing: 16.0, // 行间距 16px
children: List.generate(12, (index) => Chip(label: Text('标签$index'))),
)
8)、crossAxisAlignment - 交叉轴对齐
Wrap(
crossAxisAlignment: WrapCrossAlignment.center, // 行内项目垂直居中
children: [
Container(height: 30, width: 60, color: Colors.red),
Container(height: 50, width: 80, color: Colors.green),
Container(height: 40, width: 70, color: Colors.blue),
],
)
有点太详细啦,实际场景还未补充完,明天继续~
更多推荐




所有评论(0)