【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
章节内容【08】
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心
开发背景
背景说明要提一点:我们所有的开发耗尽2个月的时间,目前只是整合与记录并且呈现过程,大家不要想的太简单,自己试试就知道了哈,而不是你们以为的很快很简单,这点请必须要知道。
闲话不多,开源仓库地址,可以观摩已经写好的代码:
https://gitee.com/youyacao/ff-flutter
demo下载
https://www.youyacao.cn/freefirend
更新代码文件和日志文件-gitee可见
·完善了首页的列表·增加了用户中心·直播间房间的页面内容铺垫·调试变成真机调试采用android studio进行调试
本次gitpull 忘记 记录文件变化了
实战开始
先启用真机调试,进入android studio,左侧打开本项目的android目录,右侧选择device manage
ok 选择一个适配的设配,
点击加号,选择第一个创建虚拟设备。
选择pixel 9 比较适合,我们考虑的机器就是 竖屏手机,没有考虑平板
下一步提示镜像选择,继续next,
创建虚拟机的名字,默认即可
点击run 也就是播放按钮,就开始运行
在左侧的build框我们可以看见运行的过程日志
右侧我们点击 run device 可以看到运行的设备, 上面点击 run 播放按钮 可以看到 直接启动了本项目
我们看到右侧已经显示了整个模拟器情况,我们看到
首页底部的 附近的用户,以及直播列表 已经增加
其次个人中心的选项与设置均已增加,
往下可以滑动,这是整体结果情况,我们来看看具体修改的代码部分,在比较复杂的部分,这里会做解释,
项目的开发我们是用的flutter ,dart语言,因此我们继续回到vscode,android studio 只适合安卓开发很局限。
这是我们增加的页面,以及组件,本次增加大概有13个页面,
最先来看路由
import 'package:get/get.dart';
import 'package:ff_flutter/screens/index.dart';
import 'package:ff_flutter/screens/register.dart';
import 'package:ff_flutter/screens/smslogin.dart' as sms;
import 'package:ff_flutter/routes/app_routes.dart';
import 'package:ff_flutter/controllers/index_controller.dart';
import 'package:ff_flutter/controllers/register_controller.dart';
import 'package:ff_flutter/controllers/sms_login_controller.dart';
class AppPages {
static final pages = [
GetPage(
name: AppRoutes.INDEX,
page: () => IndexScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => IndexController());
}),
),
GetPage(
name: AppRoutes.REGISTER,
page: () => const RegisterScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => RegisterController());
}),
),
GetPage(
name: AppRoutes.SMS_LOGIN,
page: () => const sms.SmsLoginScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => SmsLoginController());
}),
),
];
}
先来看首页组件home_screen.dart:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:ff_flutter/screens/home/widgets/popular_live_room.dart';
import 'package:ff_flutter/screens/home/widgets/download_button.dart';
import 'package:ff_flutter/screens/home/widgets/notification_button.dart';
import 'package:ff_flutter/screens/home/widgets/big_popular_live_room.dart';
import 'package:ff_flutter/screens/home/widgets/tab_bar/index.dart';
class HomeScreenWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 顶部状态栏留空
SizedBox(height: MediaQuery.of(context).padding.top),
// Free Friend 和下载按钮
Padding(
padding: EdgeInsets.symmetric(horizontal: 32.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Free Friend",
style: TextStyle(
color: Colors.white,
fontSize: 48.sp,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
DownloadButton(),
SizedBox(width: 24.w),
NotificationButton(),
],
),
],
),
),
SizedBox(height: 32.h),
// 地区选择
Padding(
padding: EdgeInsets.symmetric(horizontal: 32.w),
child: Row(
children: [
Container(
width: 64.w,
height: 64.h,
decoration: BoxDecoration(
color: const Color(0xFFE7568C),
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
Icons.location_on,
color: Colors.white,
size: 36.sp,
),
),
SizedBox(width: 16.w),
Text(
"America",
style: TextStyle(
color: Colors.white,
fontSize: 48.sp,
fontWeight: FontWeight.bold,
),
),
],
),
),
SizedBox(height: 32.h),
// Popular Live Room 组件
PopularLiveRoom(),
BigPopularLiveRoom(),
HomeTabBar(),
],
),
);
}
}
再继续看下我们的下载按钮单独做了组件,应该下载要单独download_button.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DownloadButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 72.h,
decoration: BoxDecoration(
color: const Color(0xFFE7568C),
borderRadius: BorderRadius.circular(36.r),
),
child: TextButton.icon(
onPressed: () {},
icon: Icon(
Icons.download,
color: Colors.white,
size: 32.sp,
),
label: Text(
'Download',
style: TextStyle(
color: Colors.white,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
首页中的滑动组件,big_popular_live_room.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BigPopularLiveRoom extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(height: 50.h),
SizedBox(
height: 399.h,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: 32.w),
itemCount: 8,
itemBuilder: (context, index) {
return Container(
width: 300.w,
margin: EdgeInsets.only(right: 24.w),
child: Column(
children: [
Container(
width: 300.w,
height: 300.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.r),
),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(16.r),
child: const Image(
image: AssetImage(
'assets/images/big_popular_live_girl.png'),
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
Positioned(
right: 16.w,
bottom: 16.h,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(30.r),
),
child: Row(
children: [
Icon(
Icons.favorite,
color: Colors.white,
size: 24.sp,
),
SizedBox(width: 4.w),
Text(
'${index + 1}.2M',
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
),
),
],
),
),
),
],
),
),
SizedBox(height: 16.h),
Container(
width: 300.w,
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
[
'Bryce Adams',
'Emma Stone',
'Lucy Liu',
'Anna May'
][index % 4],
style: TextStyle(
color: Colors.white,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
// SizedBox(height: 8.h),
Text(
'@${[
'fitiajidjisd',
'emmastone',
'lucyliu',
'annamay'
][index % 4]}',
style: TextStyle(
color: Colors.grey,
fontSize: 24.sp,
),
),
],
),
),
],
),
);
},
),
),
],
);
}
}
通知按钮notification_button.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class NotificationButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 72.w,
height: 72.h,
decoration: const BoxDecoration(
color: Color(0xFF2A2A2A),
shape: BoxShape.circle,
),
child: Icon(
Icons.notifications_none,
color: Colors.white,
size: 36.sp,
),
);
}
}
再来看看 首页 index.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:ff_flutter/screens/home/index.dart';
import 'package:ff_flutter/screens/short_video_screen.dart';
import 'package:ff_flutter/screens/message_screen.dart';
import 'package:ff_flutter/screens/account/index.dart';
class IndexScreen extends StatefulWidget {
// 改为 StatefulWidget
@override
State<IndexScreen> createState() => _IndexScreenState();
}
class _IndexScreenState extends State<IndexScreen> {
int _selectedIndex = 0;
final List<Widget> _pages = [
HomeScreen(),
ShortVideoScreen(),
MessageScreen(),
AccountScreen(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF1E1E1E),
body: IndexedStack(
index: _selectedIndex,
children: _pages,
),
bottomNavigationBar: Container(
height: 168.h,
decoration: const BoxDecoration(
color: Color(0xFF1E1E1E),
),
child: Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
activeIcon: Icon(Icons.home, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.play_circle_outline),
label: 'Short Video',
activeIcon:
Icon(Icons.play_circle_outline, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_none),
label: 'Message',
activeIcon:
Icon(Icons.notifications_none, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: 'Account',
activeIcon:
Icon(Icons.person_outline, color: Color(0xFFE7568C)),
),
],
currentIndex: _selectedIndex,
selectedItemColor: const Color(0xFFE7568C),
unselectedItemColor: Colors.grey,
backgroundColor: const Color(0xFF1E1E1E),
type: BottomNavigationBarType.fixed,
selectedFontSize: 24.sp,
unselectedFontSize: 24.sp,
iconSize: 48.sp,
elevation: 0,
onTap: _onItemTapped,
),
),
),
);
}
}
class DownloadButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: () {
// 处理下载逻辑
},
icon: const Icon(
Icons.system_update_alt,
size: 30,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
label: const Text(
"Download",
style: TextStyle(
color: Color(0xfff1f1f1),
fontSize: 26,
fontFamily: "PingFang SC",
fontWeight: FontWeight.w800,
),
),
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xffe7568c), // 使用 backgroundColor 替代 primary
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(27.r),
),
padding: EdgeInsets.symmetric(horizontal: 17.w, vertical: 9.h),
minimumSize: Size(195.w, 54.h),
),
);
}
}
class CustomIconButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 54.w,
height: 54.h,
child: Stack(
children: [
Container(
width: 54.w,
height: 54.h,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xff151313),
),
),
Positioned.fill(
child: Align(
alignment: Alignment.center,
child: const Icon(
Icons.notifications,
size: 36,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
),
),
],
),
);
}
}
class MoreButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
"more",
style: TextStyle(
color: Color(0xff929292),
fontSize: 26,
),
),
SizedBox(width: 10.w),
Transform.rotate(
angle: 3.14,
child: Container(
width: 26.w,
height: 26.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.arrow_back, // 替换为合适的图标
size: 26,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
),
),
],
);
}
}
个人中心,来看头像部分
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class AccountHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 32.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 72.w,
height: 72.h,
decoration: const BoxDecoration(
color: Color(0xFF2A2A2A),
shape: BoxShape.circle,
),
child: Image.asset(
'assets/images/account_notes.png',
width: 36.w,
height: 36.h,
),
),
Container(
height: 72.h,
padding: EdgeInsets.symmetric(horizontal: 32.w),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(36.r),
border: Border.all(
color: const Color(0xFFE7568C),
width: 2.w,
),
),
child: Row(
children: [
Icon(
Icons.download,
color: const Color(0xFFE7568C),
size: 32.sp,
),
SizedBox(width: 8.w),
Text(
'Download',
style: TextStyle(
color: const Color(0xFFE7568C),
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
);
}
}
下方列表
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class MenuItem {
final String icon;
final String title;
MenuItem(this.icon, this.title);
}
class MenuList extends StatelessWidget {
final List<MenuItem> menuItems = [
MenuItem('assets/images/account_icon1.png', 'Bill'),
MenuItem('assets/images/account_icon2.png', 'Product Management'),
MenuItem('assets/images/account_icon3.png', 'Order History'),
MenuItem('assets/images/account_icon4.png', 'Historical'),
MenuItem('assets/images/account_icon5.png', 'Invite Friends'),
MenuItem(
'assets/images/account_icon6.png', 'Apply to become a broadcaster'),
];
final List<MenuItem> systemMenuItems = [
MenuItem('assets/images/account_icon7.png', 'Online Service'),
MenuItem('assets/images/account_icon8.png', 'Security Center'),
MenuItem('assets/images/account_icon9.png', 'Help Center'),
];
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 30.w),
child: Column(
children: [
...menuItems.map((item) => _buildMenuItem(item)).toList(),
Container(
height: 1.h,
color: const Color(0xFF393939),
),
SizedBox(height: 70.h),
...systemMenuItems.map((item) => _buildMenuItem(item)).toList(),
],
),
);
}
Widget _buildMenuItem(MenuItem item) {
return Column(
children: [
SizedBox(
height: 46.h,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
item.icon,
width: 48.w,
height: 48.h,
),
SizedBox(width: 24.w),
Text(
item.title,
style: TextStyle(
color: Colors.white,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
],
),
Icon(
Icons.chevron_right,
color: Colors.white,
size: 48.sp,
),
],
),
),
SizedBox(height: 70.h),
],
);
}
}
vip部分
import 'package:flutter/material.dart';
import 'package:flutter\_screenutil/flutter\_screenutil.dart';
class OpenVip extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 30.w),
child: Container(
height: 110.h,
decoration: BoxDecoration(
color: const Color(0xFF393939),
borderRadius: BorderRadius.circular(30.r),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: \[
Row(
children: \[
SizedBox(width: 32.w),
Image.asset(
'assets/images/account\_vip.png',
width: 48.w,
height: 48.h,
),
SizedBox(width: 24.w),
Text(
'Open VIP',
style: TextStyle(
color: Colors.white,
fontSize: 32.sp,
fontWeight: FontWeight.bold,
),
),
\],
),
Padding(
padding: EdgeInsets.only(right: 32.w),
child: Icon(
Icons.chevron\_right,
color: Colors.white,
size: 48.sp,
),
),
\],
),
),
);
}
}
用户信息部分、
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class UserInfo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 头像和相机按钮
Center(
child: SizedBox(
width: 319.w,
height: 319.h,
child: Stack(
children: [
Container(
width: 319.w,
height: 319.h,
decoration: const BoxDecoration(
shape: BoxShape.circle,
),
child: ClipOval(
child: Image.asset(
'assets/images/girl.png',
fit: BoxFit.cover,
),
),
),
Positioned(
right: 0,
bottom: 0,
child: Container(
width: 72.w,
height: 72.h,
decoration: const BoxDecoration(
color: Color(0xFFE7568C),
shape: BoxShape.circle,
),
child: Icon(
Icons.camera_alt,
color: Colors.white,
size: 36.sp,
),
),
),
],
),
),
),
SizedBox(height: 24.h),
// 用户名
Text(
'ANNA_122',
style: TextStyle(
color: Colors.white,
fontSize: 48.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8.h),
// 用户ID
Text(
'ID:012345548787',
style: TextStyle(
color: Colors.grey,
fontSize: 32.sp,
),
),
SizedBox(height: 8.h),
// 职业
Text(
'a teacher',
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 32.sp,
),
),
],
);
}
}
用户状态部分,
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class UserStats extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 31.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildStatItem('8.6K', 'Gold Coin'),
SizedBox(width: 120.w),
_buildStatItem('12', 'Shopping Cart'),
],
),
);
}
Widget _buildStatItem(String value, String label) {
return Column(
children: [
Text(
value,
style: TextStyle(
color: Colors.white,
fontSize: 42.sp,
fontWeight: FontWeight.w900,
),
),
SizedBox(height: 4.h),
Text(
label,
style: TextStyle(
color: Colors.grey,
fontSize: 26.sp,
),
),
],
);
}
}
整体代码中,在头像部分 还需要单独处理
这段代码定义了一个名为 AccountHeader
的无状态小部件,用于显示账户头部信息。它包含一个圆形图片和一个下载按钮。具体功能如下:
- 左侧是一个72x72的圆形容器,内含36x36的图片。
- 右侧是一个带边框的容器,内含下载图标和文本“Download”,点击效果未实现。
控制流图
mermaid
flowchart TD A[开始] --> B[创建Padding] B --> C[创建Row布局] C --> D[创建左侧圆形容器] D --> E[添加图片] C --> F[创建右侧带边框容器] F --> G[添加下载图标] G --> H[添加下载文本] H --> I[结束]
下载按钮部分也是比较值得注意的,
代码解释
这段代码定义了一个名为 DownloadButton 的无状态小部件,用于创建一个下载按钮。按钮的高度为72逻辑像素,背景颜色为粉色(0xFFE7568C),圆角半径为36逻辑像素。按钮包含一个图标和文本“Download”,点击按钮时触发空操作。
控制流图
mermaid
flowchart TD
A[开始] --> B[创建容器]
B --> C[设置容器高度和装饰]
C --> D[创建TextButton.icon]
D --> E[设置按钮点击事件为空操作]
E --> F[设置图标和文本样式]
F --> G[结束]
本次记录和学习已经完成,优雅草卓伊凡。