如何实现带TabBar的自定义AppBar及彩色环形进度条?
一、自定义带Drawer和Actions的AppBar布局
你之前把AppBar放在Stack里的写法是错误的,这会导致Scaffold无法识别AppBar,进而让Drawer触发按钮和Actions功能失效。正确的做法是将AppBar放在Scaffold的appBar属性中,再把带圆角的扩展背景、TabBar和悬浮卡片通过Stack组合在body里,完整修改后的代码如下:
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _selectedIndex = 0; void _onItemTapped(int index) { setState(() { _selectedIndex = index; }); } // 自定义颜色变量 final Color secondaryColor = const Color(0xFF2196F3); final Color mainColor = const Color(0xFFF5F5F5); @override Widget build(BuildContext context) { return DefaultTabController( length: 2, initialIndex: 0, child: Scaffold( // 正确配置Scaffold的AppBar属性 appBar: AppBar( centerTitle: true, leading: Builder( builder: (context) => IconButton( icon: const Icon(Icons.menu), onPressed: () => Scaffold.of(context).openDrawer(), ), ), // 添加右侧Actions按钮组 actions: [ IconButton( icon: const Icon(Icons.notifications), onPressed: () {}, ), IconButton( icon: const Icon(Icons.settings), onPressed: () {}, ), ], title: const Column( children: [ Text( "Total", style: TextStyle( color: Colors.white, fontSize: 16, ), ), Text( "7,560 EGP", style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), ], ), backgroundColor: secondaryColor, elevation: 0, // 去掉阴影,和下方背景无缝衔接 ), drawer: Drawer( child: ListView( padding: EdgeInsets.zero, children: [ const DrawerHeader( decoration: BoxDecoration( color: Colors.blue, ), child: Text('Drawer Header'), ), ListTile( title: const Text('Home'), selected: _selectedIndex == 0, onTap: () { _onItemTapped(0); Navigator.pop(context); }, ), ListTile( title: const Text('Business'), selected: _selectedIndex == 1, onTap: () { _onItemTapped(1); Navigator.pop(context); }, ), ListTile( title: const Text('School'), selected: _selectedIndex == 2, onTap: () { _onItemTapped(2); Navigator.pop(context); }, ), ], ), ), body: Stack( children: [ // 带圆角的扩展背景+TabBar Column( children: [ Container( width: double.infinity, decoration: BoxDecoration( color: secondaryColor, borderRadius: const BorderRadius.vertical( bottom: Radius.circular(20), ), ), padding: const EdgeInsets.only(bottom: 20), child: const TabBar( indicatorColor: Colors.amberAccent, indicatorWeight: 5, unselectedLabelColor: Colors.white70, tabs: [ Text( "Overview", style: TextStyle(color: Colors.white), ), Text( "Details", style: TextStyle(color: Colors.white), ), ], ), ), const SizedBox(height: 80), // 占位避免内容被卡片遮挡 ], ), // 悬浮卡片 Positioned( // 适配不同设备状态栏高度+AppBar高度 top: MediaQuery.of(context).padding.top + kToolbarHeight + 40, left: 20, right: 20, child: Card( elevation: 4, color: mainColor, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: List.generate(8, (index) => const Text("Sample Content")), ), ), ), ), // TabBar对应的内容区域 const Padding( padding: EdgeInsets.only(top: 200), child: TabBarView( children: [ Center(child: Text("Overview Tab Content")), Center(child: Text("Details Tab Content")), ], ), ), ], ), ), ); } }
关键修改说明:
- 将AppBar移至Scaffold的
appBar属性,配置leading触发Drawer,添加actions按钮组 - 移除AppBar阴影,让其和下方圆角背景无缝衔接
- 动态计算悬浮卡片的top位置,适配不同设备的状态栏高度
- 补充TabBarView展示标签对应的内容
二、彩色环形进度条实现
Flutter自带的CircularProgressIndicator不支持直接设置渐变,推荐两种实现方式:
方式1:自定义CustomPaint绘制
import 'dart:math' as math; import 'package:flutter/material.dart'; class ColoredCircularProgress extends StatelessWidget { final double progress; // 进度值(0-1) final List<Color> colors; // 渐变颜色数组 final double strokeWidth; const ColoredCircularProgress({ super.key, required this.progress, this.colors = const [Colors.red, Colors.yellow, Colors.green], this.strokeWidth = 8, }); @override Widget build(BuildContext context) { return CustomPaint( size: const Size(100, 100), painter: _ColoredProgressPainter( progress: progress, colors: colors, strokeWidth: strokeWidth, ), ); } } class _ColoredProgressPainter extends CustomPainter { final double progress; final List<Color> colors; final double strokeWidth; _ColoredProgressPainter({ required this.progress, required this.colors, required this.strokeWidth, }); @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = (size.width - strokeWidth) / 2; // 绘制背景圆环 final backgroundPaint = Paint() ..color = Colors.grey.shade200 ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round; canvas.drawCircle(center, radius, backgroundPaint); // 绘制彩色进度圆环 final gradient = SweepGradient( colors: colors, startAngle: -math.pi / 2, endAngle: math.pi * 1.5, ); final progressPaint = Paint() ..shader = gradient.createShader(Rect.fromCircle(center: center, radius: radius)) ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round; final arcAngle = 2 * math.pi * progress; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -math.pi / 2, arcAngle, false, progressPaint, ); } @override bool shouldRepaint(covariant _ColoredProgressPainter oldDelegate) { return oldDelegate.progress != progress; } }
使用示例:
ColoredCircularProgress( progress: 0.75, // 75%进度 colors: [Colors.blue, Colors.purple, Colors.pink], )
方式2:用ShaderMask包装默认进度条
如果不想自定义Painter,可通过ShaderMask给默认进度条添加渐变:
import 'dart:math' as math; import 'package:flutter/material.dart'; ShaderMask( shaderCallback: (bounds) => const SweepGradient( colors: [Colors.orange, Colors.red, Colors.purple], startAngle: 0, endAngle: 2 * math.pi, ).createShader(bounds), child: const CircularProgressIndicator( value: 0.6, strokeWidth: 8, valueColor: AlwaysStoppedAnimation(Colors.white), backgroundColor: Colors.grey.shade200, ), )
内容的提问来源于stack exchange,提问作者Emad Younan
相关产品推荐
相关产品推荐

