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(),
debugShowCheckedModeBanner: false,
theme: ThemeData(
scaffoldBackgroundColor: Color(0xFFF5F5F5),
useMaterial3: true,
fontFamily: 'CustomFont',
),
);

View File

@@ -84,7 +84,7 @@ class _BlogPageState extends State<BlogPage> {
);
}
return ListView.builder(
return ListView.separated(
controller: _scrollController,
itemCount: provider.blogList.length,
itemBuilder: (context, index) {
@@ -94,6 +94,9 @@ class _BlogPageState extends State<BlogPage> {
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) {
return ListView.builder(
return ListView.separated(
itemCount: provider.blogList.length,
itemBuilder: (context, index) {
final blog = provider.blogList[index];
@@ -33,6 +33,9 @@ class _CategoryListPageState extends State<CategoryListPage> {
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),
// 分类列表
Expanded(
child: ListView.builder(
child: ListView.separated(
itemCount: provider.categoryList.length,
itemBuilder: (context, index) {
final category = provider.categoryList[index];
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:blog_app/provider/blog.dart';
import 'package:blog_app/widget/blog.dart';
@@ -131,29 +132,35 @@ class StatsPageState extends State<StatsPage> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
children: [
buildStatsCard(
icon: Icons.article,
context: context,
title: '博客数量',
count: provider.overviewStats.blogCount,
count: provider.overviewStats.blogCount.toString(),
unit: '',
),
buildStatsCard(
icon: Icons.folder,
context: context,
title: '分类数量',
count: provider.overviewStats.categoryCount,
count: provider.overviewStats.categoryCount.toString(),
unit: '',
),
buildStatsCard(
icon: Icons.remove_red_eye,
context: context,
title: '访问量',
count: provider.overviewStats.visitCount,
count: formatChineseDecimal(provider.overviewStats.visitCount),
unit: '',
),
buildStatsCard(
icon: Icons.text_fields,
context: context,
title: '总字数',
count: provider.overviewStats.wordCount,
count: formatChineseDecimal(provider.overviewStats.wordCount),
unit: '',
),
],
@@ -181,44 +188,32 @@ class StatsPageState extends State<StatsPage> {
});
},
),
const SizedBox(height: 6),
const SizedBox(height: 8),
_buildBlogStatsGrid(provider),
const SizedBox(height: 6),
const SizedBox(height: 8),
SizedBox(
height: chartHeight,
child: buildCard(
context: context,
child: _buildApprovedStats(provider),
),
child: BuildCard(child: _buildApprovedStats(provider)),
),
const SizedBox(height: 6),
const SizedBox(height: 8),
SizedBox(
height: chartHeight,
child: buildCard(context: context, child: _buildVisitStats(provider)),
child: BuildCard(child: _buildVisitStats(provider)),
),
const SizedBox(height: 6),
const SizedBox(height: 8),
SizedBox(
height: chartHeight,
child: buildCard(
context: context,
child: _buildVisitRankStats(provider),
),
child: BuildCard(child: _buildVisitRankStats(provider)),
),
const SizedBox(height: 6),
const SizedBox(height: 8),
SizedBox(
height: chartHeight,
child: buildCard(
context: context,
child: _buildCategoryStats(provider),
),
child: BuildCard(child: _buildCategoryStats(provider)),
),
const SizedBox(height: 6),
const SizedBox(height: 8),
SizedBox(
height: chartHeight,
child: buildCard(
context: context,
child: _buildReadRankStats(provider),
),
child: BuildCard(child: _buildReadRankStats(provider)),
),
],
);
@@ -233,8 +228,8 @@ class StatsPageState extends State<StatsPage> {
if (blogProvider.isLoading)
buildLoadingIndicator()
else
SingleChildScrollView(child: _buildContent(blogProvider))
]
SingleChildScrollView(child: _buildContent(blogProvider)),
],
);
}
}

View File

@@ -28,7 +28,7 @@ class TimelinePageState extends State<TimelinePage> {
return Timeline.tileBuilder(
theme: TimelineThemeData(nodePosition: 0, indicatorPosition: 0),
padding: EdgeInsets.all(6),
padding: EdgeInsets.all(0),
builder: TimelineTileBuilder.connected(
itemCount: provider.blogList.length,
connectorBuilder:
@@ -39,9 +39,12 @@ class TimelinePageState extends State<TimelinePage> {
},
contentsBuilder: (context, index) {
final blog = provider.blogList[index];
return InkWell(
onTap: () => navigatorToBlogDetail(context, blog.id),
child: buildBlogListItem(context, blog, index + 1),
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
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}',
);
}
/// 将数字格式化为中文单位(万/亿)
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,
children: [
if (blog.isGreat)
_buildBlogLabelItem('精品', Icons.workspace_premium, Colors.amber),
_buildBlogLabelItem('精品', Icons.workspace_premium, Colors.deepOrange),
if (blog.topValue > 1)
_buildBlogLabelItem('置顶', Icons.push_pin, Colors.red),
_buildBlogLabelItem(blog.category, Icons.folder, Colors.blue),
@@ -114,26 +114,20 @@ class BlogCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return buildCard(
context: context,
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildBlogTitle(context),
SizedBox(height: 16),
_buildBlogLabel(),
SizedBox(height: 16),
Container(
height: 1,
width: 80,
color: Theme.of(context).colorScheme.primary,
),
SizedBox(height: 16),
_buildBlogSummary(),
],
),
final colors = Theme.of(context).colorScheme;
return BuildCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildBlogTitle(context),
SizedBox(height: 16),
_buildBlogLabel(),
SizedBox(height: 16),
Container(height: 1, width: 80, color: colors.primary),
SizedBox(height: 16),
_buildBlogSummary(),
],
),
);
}
@@ -164,8 +158,7 @@ Widget buildCategoryTitle(BuildContext context, int count) {
Widget buildCategoryCard(BuildContext context, BlogCategory category) {
final colors = Theme.of(context).colorScheme;
return buildCard(
context: context,
return BuildCard(
child: ListTile(
leading: Container(
width: 50,
@@ -222,18 +215,12 @@ Widget buildListTitle(int count) {
Widget buildBlogListItem(BuildContext context, Blog blog, int index) {
final colors = Theme.of(context).colorScheme;
return buildCard(
context: context,
return BuildCard(
child: Padding(
padding: EdgeInsets.all(10),
padding: EdgeInsets.all(0),
child: Row(
children: [
Text(
'$index.',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
Text('$index.', style: TextStyle(fontWeight: FontWeight.w500)),
SizedBox(width: 10),
Text(
formatDateString(blog.createTime),
@@ -260,48 +247,65 @@ Widget buildBlogListItem(BuildContext context, Blog blog, int index) {
Widget buildStatsCard({
required BuildContext context,
required IconData icon,
required String title,
required int count,
required String count,
required String unit,
}) {
return buildCard(
context: context,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
final colors = Theme.of(context).colorScheme;
return BuildCard(
child: Row(
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colors.primary.withAlpha(50),
shape: BoxShape.circle,
),
child: Icon(icon, size: 26, color: colors.primary),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
count.toString(),
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.0,
SizedBox(width: 6),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(width: 4),
Text(
unit,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
const SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
count,
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';
Widget buildCard({required BuildContext context, required Widget child}) {
final colors = Theme.of(context).colorScheme;
return Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
width: double.infinity,
class BuildCard extends StatelessWidget {
final Widget? child;
const BuildCard({super.key, this.child});
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: colors.surface,
color: colors.surfaceContainer,
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({

View File

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