效果图
详细介绍:整体分为3个部分,一个是顶部固定的信息栏,一个是滑动的列表,还有一个是底部固定的播放栏。
重点内容
// 理解基础布局组件 Stack( // 层叠布局:背景+内容+底部栏 children: [ Container(), // 背景层 CustomScrollView(), // 内容滚动层 Positioned() // 底部固定层 ], ) // 理解滚动布局 CustomScrollView( slivers: [ SliverAppBar(), // 固定头部 SliverToBoxAdapter(), // 列表内容 ], ) // 理解组件封装 Widget buildItem({ // 参数化组件 required String title, required String icon, VoidCallback? callback }) { return Container(...); } // 理解状态管理 class SleepPage extends StatefulWidget { @override State<StatefulWidget> createState() => _SleepPage(); }实现步骤
1.背景渐变
Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.white, Colors.purple.withOpacity(0.7), ], ), ), ),2.可滚动的内容
CustomScrollView( slivers: [ // 顶部信息固定区域 SliverAppBar( expandedHeight: 270, //展开高度 collapsedHeight: 270, //收起高度(与展开相同=固定高度) pinned: true, //固定在顶部,不让滑动 snap: false, floating: false, automaticallyImplyLeading: false, backgroundColor: Colors.transparent, elevation: 0, flexibleSpace: Stack( //实际内容区 children: [ //背景容器 Positioned.fill( child: Image.asset( "assets/images/start.png", fit: BoxFit.cover, ), ), // 标题行 Positioned( top:50, left: 10, right: 0, child: Row( children: [ SizedBox(width: 20), GestureDetector( onTap: () => Navigator.pop(context), child: Image.asset("assets/images/back_white.png",width: 20,height: 20,), ), Spacer(), Text( "标题栏", style: TextStyle(color: Colors.white, fontSize: 24), ), Spacer(), SizedBox(width: 20), ], ), ), // 耳机图 Positioned( top: 109, left: 0, right: 0, child: Container( height: 141, child: Image.asset( "assets/images/earphone.png", fit: BoxFit.contain, ), ), ), ], ), ), // 内容列表 SliverToBoxAdapter( child: Column( children: [ buildItem( title: "列表1", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表2", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表3", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表4", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表5", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表6", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表7", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表8", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表9", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), // 添加底部间距,为固定播放栏留出空间 const SizedBox(height: 80), // 播放栏高度 + 边距 ], ), ), ], ),3.固定在底部的播放栏
Positioned( left: 10, right: 15, bottom: 15, // 距离底部的距离 child: Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: const Color(0xFF2C2C2E), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Image.asset("assets/images/cherry.png",), const SizedBox(width: 10), const Text( "Rain", style: TextStyle(color: Colors.white), ), const Spacer(), Image.asset("assets/images/mango.png",), const SizedBox(width: 10), Image.asset("assets/images/banana.png"), ], ), ), ),4.小卡片的通用组件
Widget buildItem({ required String title, required String icon, required String rightIcon, VoidCallback? callback }) { return Container( padding: EdgeInsets.symmetric(horizontal: 20), margin: EdgeInsets.only(left: 15,right: 20), height: 52, width: double.infinity, decoration:BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Material( color: Colors.transparent, child: Ink( child: InkWell( onTap: callback, child: Row( children: [ Image.asset(icon), SizedBox(width: 15,), Text(title,style: const TextStyle(color: Colors.white,fontSize: 18),), const Expanded(child: SizedBox()), Image.asset(rightIcon), ], ) ), ), ) ); }代码实例
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class SleepPage extends StatefulWidget { const SleepPage({super.key}); @override State<StatefulWidget> createState() => _SleepPage(); } class _SleepPage extends State<SleepPage> { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Stack( children: [ //1.背景渐变 Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.white, Colors.purple.withOpacity(0.7), ], ), ), ), // 2. 可滚动内容 CustomScrollView( slivers: [ // 顶部信息固定区域 SliverAppBar( expandedHeight: 270, //展开高度 collapsedHeight: 270, //收起高度(与展开相同=固定高度) pinned: true, //固定在顶部,不让滑动 snap: false, floating: false, automaticallyImplyLeading: false, backgroundColor: Colors.transparent, elevation: 0, flexibleSpace: Stack( //实际内容区 children: [ //背景容器 Positioned.fill( child: Image.asset( "assets/images/start.png", fit: BoxFit.cover, ), ), // 标题行 Positioned( top:50, left: 10, right: 0, child: Row( children: [ SizedBox(width: 20), GestureDetector( onTap: () => Navigator.pop(context), child: Image.asset("assets/images/back_white.png",width: 20,height: 20,), ), Spacer(), Text( "标题栏", style: TextStyle(color: Colors.white, fontSize: 24), ), Spacer(), SizedBox(width: 20), ], ), ), // 耳机图 Positioned( top: 109, left: 0, right: 0, child: Container( height: 141, child: Image.asset( "assets/images/earphone.png", fit: BoxFit.contain, ), ), ), ], ), ), // 内容列表 SliverToBoxAdapter( child: Column( children: [ buildItem( title: "列表1", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表2", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表3", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表4", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表5", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表6", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表7", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表8", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), buildItem( title: "列表9", icon: "assets/images/apple.png", rightIcon: "assets/images/apple.png", ), const SizedBox(height: 8), // 添加底部间距,为固定播放栏留出空间 const SizedBox(height: 80), // 播放栏高度 + 边距 ], ), ), ], ), // 3. 固定在底部的播放栏 Positioned( left: 10, right: 15, bottom: 15, // 距离底部的距离 child: Container( height: 60, padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: const Color(0xFF2C2C2E), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Image.asset("assets/images/cherry.png",), const SizedBox(width: 10), const Text( "Rain", style: TextStyle(color: Colors.white), ), const Spacer(), Image.asset("assets/images/mango.png",), const SizedBox(width: 10), Image.asset("assets/images/banana.png"), ], ), ), ), ], ), ); } //功能小卡片 Widget buildItem({ required String title, required String icon, required String rightIcon, VoidCallback? callback }) { return Container( padding: EdgeInsets.symmetric(horizontal: 20), margin: EdgeInsets.only(left: 15,right: 20), height: 52, width: double.infinity, decoration:BoxDecoration( color: Colors.white.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Material( color: Colors.transparent, child: Ink( child: InkWell( onTap: callback, child: Row( children: [ Image.asset(icon), SizedBox(width: 15,), Text(title,style: const TextStyle(color: Colors.white,fontSize: 18),), const Expanded(child: SizedBox()), Image.asset(rightIcon), ], ) ), ), ) ); } }