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 createState() => _BlogDetailPageState(); } class _BlogDetailPageState extends State { final tocController = TocController(); bool _showToc = false; late Future _blogDetail; @override void initState() { super.initState(); _blogDetail = _loadBlogDetail(); } Future _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( 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(), ], ); } }