156 lines
4.3 KiB
Dart
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(),
|
|
],
|
|
);
|
|
}
|
|
}
|