Files
blog_app/lib/pages/blog_detail_page.dart
2025-11-15 13:03:10 +08:00

156 lines
4.3 KiB
Dart

import 'package:blog_app/apis/blog.dart';
import 'package:blog_app/models/blog.dart';
import 'package:flutter/material.dart';
import 'package:markdown_widget/config/toc.dart';
import 'package:markdown_widget/widget/markdown.dart';
class BlogDetailPage extends StatefulWidget {
final int blogId;
const BlogDetailPage({super.key, required this.blogId});
@override
State<BlogDetailPage> createState() => _BlogDetailPageState();
}
class _BlogDetailPageState extends State<BlogDetailPage> {
final tocController = TocController();
bool _showToc = false;
late Future<Blog> _blogDetail;
@override
void initState() {
super.initState();
_blogDetail = _loadBlogDetail();
}
Future<Blog> _loadBlogDetail() async {
try {
return await queryBlogByIdApi(widget.blogId);
} catch (e) {
throw Exception('获取博客详情失败: $e');
}
}
Widget _buildBlogTitle(String title) {
return Text(
title,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
);
}
Widget _buildTocPanel() => Visibility(
visible: _showToc,
child: Align(
alignment: Alignment.bottomRight,
child: Container(
width: 250,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(50),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
border: Border.all(color: Colors.grey[300]!),
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'目录',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
],
),
),
Expanded(
child: TocWidget(
controller: tocController,
tocTextStyle: TextStyle(fontSize: 14),
),
),
],
),
),
),
);
Widget _buildMarkdown(String data) =>
MarkdownWidget(data: data, tocController: tocController);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('博客详情')),
body: Container(
padding: EdgeInsets.all(16),
child: FutureBuilder<Blog>(
future: _blogDetail,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('加载失败: ${snapshot.error}')],
),
);
}
if (!snapshot.hasData) {
return Center(child: Text('暂无数据'));
}
final blogDetail = snapshot.data!;
return _buildBlogDetailContent(context, blogDetail);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _showToc = !_showToc),
mini: true,
backgroundColor: Theme.of(context).primaryColor,
child: Icon(_showToc ? Icons.close : Icons.list, color: Colors.white),
),
);
}
Widget _buildBlogDetailContent(BuildContext context, Blog blogDetail) {
return Stack(
children: [
Column(
children: [
_buildBlogTitle(blogDetail.title),
SizedBox(height: 10),
Expanded(child: _buildMarkdown(blogDetail.content!)),
],
),
_buildTocPanel(),
],
);
}
}