factor:布局颜色重构

This commit is contained in:
2025-11-20 13:37:56 +08:00
parent 50d7657a93
commit 8f0f641d37
10 changed files with 159 additions and 118 deletions

View File

@@ -27,7 +27,7 @@ class MyApp extends StatelessWidget {
home: HomePage(), home: HomePage(),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(
scaffoldBackgroundColor: Color(0xFFF5F5F5), useMaterial3: true,
fontFamily: 'CustomFont', fontFamily: 'CustomFont',
), ),
); );

View File

@@ -84,7 +84,7 @@ class _BlogPageState extends State<BlogPage> {
); );
} }
return ListView.builder( return ListView.separated(
controller: _scrollController, controller: _scrollController,
itemCount: provider.blogList.length, itemCount: provider.blogList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -94,6 +94,9 @@ class _BlogPageState extends State<BlogPage> {
child: BlogCard(blog: blog), child: BlogCard(blog: blog),
); );
}, },
separatorBuilder: (BuildContext context, int index) {
return SizedBox(height: 8);
},
); );
} }

View File

@@ -24,7 +24,7 @@ class _CategoryListPageState extends State<CategoryListPage> {
} }
Widget _buildBlogList(BlogProvider provider) { Widget _buildBlogList(BlogProvider provider) {
return ListView.builder( return ListView.separated(
itemCount: provider.blogList.length, itemCount: provider.blogList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final blog = provider.blogList[index]; final blog = provider.blogList[index];
@@ -33,6 +33,9 @@ class _CategoryListPageState extends State<CategoryListPage> {
child: buildBlogListItem(context, blog, index + 1), child: buildBlogListItem(context, blog, index + 1),
); );
}, },
separatorBuilder: (BuildContext context, int index) {
return SizedBox(height: 8);
},
); );
} }

View File

@@ -36,12 +36,15 @@ class _CategoryPageState extends State<CategoryPage> {
SizedBox(height: 10), SizedBox(height: 10),
// 分类列表 // 分类列表
Expanded( Expanded(
child: ListView.builder( child: ListView.separated(
itemCount: provider.categoryList.length, itemCount: provider.categoryList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final category = provider.categoryList[index]; final category = provider.categoryList[index];
return buildCategoryCard(context, category); return buildCategoryCard(context, category);
}, },
separatorBuilder: (BuildContext context, int index) {
return SizedBox(height: 8);
},
), ),
), ),
], ],

View File

@@ -1,3 +1,4 @@
import 'package:blog_app/utils/index.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:blog_app/provider/blog.dart'; import 'package:blog_app/provider/blog.dart';
import 'package:blog_app/widget/blog.dart'; import 'package:blog_app/widget/blog.dart';
@@ -131,29 +132,35 @@ class StatsPageState extends State<StatsPage> {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 2, childAspectRatio: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
children: [ children: [
buildStatsCard( buildStatsCard(
icon: Icons.article,
context: context, context: context,
title: '博客数量', title: '博客数量',
count: provider.overviewStats.blogCount, count: provider.overviewStats.blogCount.toString(),
unit: '', unit: '',
), ),
buildStatsCard( buildStatsCard(
icon: Icons.folder,
context: context, context: context,
title: '分类数量', title: '分类数量',
count: provider.overviewStats.categoryCount, count: provider.overviewStats.categoryCount.toString(),
unit: '', unit: '',
), ),
buildStatsCard( buildStatsCard(
icon: Icons.remove_red_eye,
context: context, context: context,
title: '访问量', title: '访问量',
count: provider.overviewStats.visitCount, count: formatChineseDecimal(provider.overviewStats.visitCount),
unit: '', unit: '',
), ),
buildStatsCard( buildStatsCard(
icon: Icons.text_fields,
context: context, context: context,
title: '总字数', title: '总字数',
count: provider.overviewStats.wordCount, count: formatChineseDecimal(provider.overviewStats.wordCount),
unit: '', unit: '',
), ),
], ],
@@ -181,44 +188,32 @@ class StatsPageState extends State<StatsPage> {
}); });
}, },
), ),
const SizedBox(height: 6), const SizedBox(height: 8),
_buildBlogStatsGrid(provider), _buildBlogStatsGrid(provider),
const SizedBox(height: 6), const SizedBox(height: 8),
SizedBox( SizedBox(
height: chartHeight, height: chartHeight,
child: buildCard( child: BuildCard(child: _buildApprovedStats(provider)),
context: context,
child: _buildApprovedStats(provider),
),
), ),
const SizedBox(height: 6), const SizedBox(height: 8),
SizedBox( SizedBox(
height: chartHeight, height: chartHeight,
child: buildCard(context: context, child: _buildVisitStats(provider)), child: BuildCard(child: _buildVisitStats(provider)),
), ),
const SizedBox(height: 6), const SizedBox(height: 8),
SizedBox( SizedBox(
height: chartHeight, height: chartHeight,
child: buildCard( child: BuildCard(child: _buildVisitRankStats(provider)),
context: context,
child: _buildVisitRankStats(provider),
),
), ),
const SizedBox(height: 6), const SizedBox(height: 8),
SizedBox( SizedBox(
height: chartHeight, height: chartHeight,
child: buildCard( child: BuildCard(child: _buildCategoryStats(provider)),
context: context,
child: _buildCategoryStats(provider),
),
), ),
const SizedBox(height: 6), const SizedBox(height: 8),
SizedBox( SizedBox(
height: chartHeight, height: chartHeight,
child: buildCard( child: BuildCard(child: _buildReadRankStats(provider)),
context: context,
child: _buildReadRankStats(provider),
),
), ),
], ],
); );
@@ -233,8 +228,8 @@ class StatsPageState extends State<StatsPage> {
if (blogProvider.isLoading) if (blogProvider.isLoading)
buildLoadingIndicator() buildLoadingIndicator()
else else
SingleChildScrollView(child: _buildContent(blogProvider)) SingleChildScrollView(child: _buildContent(blogProvider)),
] ],
); );
} }
} }

View File

@@ -28,7 +28,7 @@ class TimelinePageState extends State<TimelinePage> {
return Timeline.tileBuilder( return Timeline.tileBuilder(
theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0), theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0),
padding: EdgeInsets.all(6), padding: EdgeInsets.all(0),
builder: TimelineTileBuilder.connected( builder: TimelineTileBuilder.connected(
itemCount: provider.blogList.length, itemCount: provider.blogList.length,
connectorBuilder: connectorBuilder:
@@ -39,9 +39,12 @@ class TimelinePageState extends State<TimelinePage> {
}, },
contentsBuilder: (context, index) { contentsBuilder: (context, index) {
final blog = provider.blogList[index]; final blog = provider.blogList[index];
return InkWell( return Padding(
onTap: () => navigatorToBlogDetail(context, blog.id), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: buildBlogListItem(context, blog, index + 1), child: InkWell(
onTap: () => navigatorToBlogDetail(context, blog.id),
child: buildBlogListItem(context, blog, index + 1),
),
); );
}, },
), ),

View File

@@ -26,3 +26,28 @@ PageResult<T> convertPageResponse<T>(
'Expected a Map for page response conversion, but got ${data.runtimeType}', 'Expected a Map for page response conversion, but got ${data.runtimeType}',
); );
} }
/// 将数字格式化为中文单位(万/亿)
String formatChineseDecimal(num number, {int decimalPlaces = 2}) {
if (number < 10000) {
return number.toString();
} else if (number < 100000000) {
// 万单位
double result = number / 10000.0;
return '${_formatDecimal(result, decimalPlaces)}';
} else {
// 亿单位
double result = number / 100000000.0;
return '${_formatDecimal(result, decimalPlaces)}亿';
}
}
String _formatDecimal(double number, int decimalPlaces) {
// 去除末尾的0和小数点
String formatted = number.toStringAsFixed(decimalPlaces);
if (formatted.contains('.')) {
formatted = formatted.replaceAll(RegExp(r'0*$'), '');
formatted = formatted.replaceAll(RegExp(r'\.$'), '');
}
return formatted;
}

View File

@@ -53,7 +53,7 @@ class BlogCard extends StatelessWidget {
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
children: [ children: [
if (blog.isGreat) if (blog.isGreat)
_buildBlogLabelItem('精品', Icons.workspace_premium, Colors.amber), _buildBlogLabelItem('精品', Icons.workspace_premium, Colors.deepOrange),
if (blog.topValue > 1) if (blog.topValue > 1)
_buildBlogLabelItem('置顶', Icons.push_pin, Colors.red), _buildBlogLabelItem('置顶', Icons.push_pin, Colors.red),
_buildBlogLabelItem(blog.category, Icons.folder, Colors.blue), _buildBlogLabelItem(blog.category, Icons.folder, Colors.blue),
@@ -114,26 +114,20 @@ class BlogCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return buildCard( final colors = Theme.of(context).colorScheme;
context: context,
child: Padding( return BuildCard(
padding: EdgeInsets.all(12), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, children: [
children: [ _buildBlogTitle(context),
_buildBlogTitle(context), SizedBox(height: 16),
SizedBox(height: 16), _buildBlogLabel(),
_buildBlogLabel(), SizedBox(height: 16),
SizedBox(height: 16), Container(height: 1, width: 80, color: colors.primary),
Container( SizedBox(height: 16),
height: 1, _buildBlogSummary(),
width: 80, ],
color: Theme.of(context).colorScheme.primary,
),
SizedBox(height: 16),
_buildBlogSummary(),
],
),
), ),
); );
} }
@@ -164,8 +158,7 @@ Widget buildCategoryTitle(BuildContext context, int count) {
Widget buildCategoryCard(BuildContext context, BlogCategory category) { Widget buildCategoryCard(BuildContext context, BlogCategory category) {
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
return buildCard( return BuildCard(
context: context,
child: ListTile( child: ListTile(
leading: Container( leading: Container(
width: 50, width: 50,
@@ -222,18 +215,12 @@ Widget buildListTitle(int count) {
Widget buildBlogListItem(BuildContext context, Blog blog, int index) { Widget buildBlogListItem(BuildContext context, Blog blog, int index) {
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
return buildCard( return BuildCard(
context: context,
child: Padding( child: Padding(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(0),
child: Row( child: Row(
children: [ children: [
Text( Text('$index.', style: TextStyle(fontWeight: FontWeight.w500)),
'$index.',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
SizedBox(width: 10), SizedBox(width: 10),
Text( Text(
formatDateString(blog.createTime), formatDateString(blog.createTime),
@@ -260,48 +247,65 @@ Widget buildBlogListItem(BuildContext context, Blog blog, int index) {
Widget buildStatsCard({ Widget buildStatsCard({
required BuildContext context, required BuildContext context,
required IconData icon,
required String title, required String title,
required int count, required String count,
required String unit, required String unit,
}) { }) {
return buildCard( final colors = Theme.of(context).colorScheme;
context: context,
child: Column( return BuildCard(
mainAxisAlignment: MainAxisAlignment.center, child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Container(
title, padding: const EdgeInsets.all(12),
style: TextStyle( decoration: BoxDecoration(
fontSize: 16, color: colors.primary.withAlpha(50),
color: Colors.grey.shade600, shape: BoxShape.circle,
fontWeight: FontWeight.w500,
), ),
child: Icon(icon, size: 26, color: colors.primary),
), ),
const SizedBox(height: 12), SizedBox(width: 6),
Row( Expanded(
mainAxisAlignment: MainAxisAlignment.center, child: Column(
crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.center,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Text( children: [
count.toString(), Text(
style: const TextStyle( title,
fontSize: 28, style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16,
color: Colors.black87, color: Colors.grey.shade600,
height: 1.0, fontWeight: FontWeight.w500,
),
), ),
), const SizedBox(height: 6),
const SizedBox(width: 4), Row(
Text( mainAxisAlignment: MainAxisAlignment.center,
unit, crossAxisAlignment: CrossAxisAlignment.end,
style: TextStyle( children: [
fontSize: 14, Text(
color: Colors.grey.shade600, count,
fontWeight: FontWeight.w500, style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: colors.primary,
height: 1.0,
),
),
const SizedBox(width: 4),
Text(
unit,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
],
), ),
), ],
], ),
), ),
], ],
), ),

View File

@@ -1,20 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Widget buildCard({required BuildContext context, required Widget child}) { class BuildCard extends StatelessWidget {
final colors = Theme.of(context).colorScheme; final Widget? child;
return Card(
elevation: 0, const BuildCard({super.key, this.child});
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container( @override
width: double.infinity, Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: colors.surface, color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withAlpha(50), width: 1),
), ),
child: Padding(padding: EdgeInsets.all(8), child: child), padding: const EdgeInsets.all(16),
), child: child,
); );
}
} }
Widget circleIconButton({ Widget circleIconButton({

View File

@@ -27,8 +27,7 @@ Widget buildSearchField({
} }
Widget buildSearchResultItem(BuildContext context, BlogSearch blog) { Widget buildSearchResultItem(BuildContext context, BlogSearch blog) {
return buildCard( return BuildCard(
context: context,
child: ListTile( child: ListTile(
title: Text( title: Text(
blog.title, blog.title,
@@ -56,11 +55,14 @@ Widget buildSearchResultList(
BuildContext context, BuildContext context,
List<BlogSearch> searchResultList, List<BlogSearch> searchResultList,
) { ) {
return ListView.builder( return ListView.separated(
itemCount: searchResultList.length, itemCount: searchResultList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return buildSearchResultItem(context, searchResultList[index]); return buildSearchResultItem(context, searchResultList[index]);
}, },
separatorBuilder: (BuildContext context, int index) {
return SizedBox(height: 8);
},
); );
} }