Flutter 框架跨平台鸿蒙开发 - 笑话大全应用开发教程
分类筛选:使用计算属性实现高效过滤随机功能:Random类实现随机笑话剪贴板操作:Clipboard API实现复制分享渐变设计:LinearGradient提升视觉效果:使用最新的NavigationBar组件卡片布局:Card和InkWell实现优雅交互。
Flutter笑话大全应用开发教程
项目简介
这是一款轻松娱乐的笑话应用,收录了多种类型的笑话,包括冷笑话、脑筋急转弯、幽默段子、爆笑笑话、经典笑话和搞笑对话。应用提供分类浏览、随机笑话、收藏功能和分享功能,界面简洁友好,让用户随时随地开心一刻。
运行效果图



核心特性
- 分类浏览:6大笑话分类,20+精选笑话
- 随机笑话:一键获取随机笑话,惊喜不断
- 收藏功能:收藏喜欢的笑话,随时回看
- 复制分享:一键复制笑话内容,分享给朋友
- 点赞统计:显示笑话受欢迎程度
- 详情页面:完整展示笑话内容
- 渐变设计:橙色主题,温暖活泼
- 流畅交互:卡片式布局,操作便捷
技术栈
- Flutter 3.x
- Material Design 3
- 状态管理(setState)
- 剪贴板操作
- 列表过滤
项目架构
数据模型设计
Joke(笑话模型)
class Joke {
final int id; // 笑话ID
final String title; // 标题
final String content; // 内容
final String category; // 分类
final int likes; // 点赞数
bool isFavorite; // 是否收藏
Joke({
required this.id,
required this.title,
required this.content,
required this.category,
this.likes = 0,
this.isFavorite = false,
});
}
设计要点:
- ID用于唯一标识
- 标题和内容分离,便于列表展示
- 分类用于筛选
- 点赞数展示受欢迎程度
- isFavorite标记收藏状态
笑话分类
final List<String> _categories = [
'全部',
'冷笑话',
'脑筋急转弯',
'幽默段子',
'爆笑笑话',
'经典笑话',
'搞笑对话'
];
分类说明:
- 冷笑话:需要思考才能理解的笑话
- 脑筋急转弯:考验思维的问答笑话
- 幽默段子:生活中的幽默小故事
- 爆笑笑话:让人捧腹大笑的笑话
- 经典笑话:流传已久的经典笑话
- 搞笑对话:有趣的对话场景
核心功能实现
1. 分类筛选
使用计算属性过滤笑话列表。
List<Joke> get _filteredJokes {
if (_selectedCategory == '全部') {
return _jokes;
}
return _jokes.where((joke) => joke.category == _selectedCategory).toList();
}
分类标签栏:
Widget _buildCategoryTabs() {
return Container(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
final category = categories[index];
final isSelected = category == selectedCategory;
return GestureDetector(
onTap: () => onCategoryChanged(category),
child: Container(
decoration: BoxDecoration(
color: isSelected ? Colors.orange : Colors.grey.shade200,
borderRadius: BorderRadius.circular(20),
),
child: Text(
category,
style: TextStyle(
color: isSelected ? Colors.white : Colors.grey.shade700,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
),
);
},
),
);
}
2. 笑话卡片设计
Widget _buildJokeCard(BuildContext context, Joke joke) {
return Card(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => JokeDetailPage(joke: joke),
),
);
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 分类标签和点赞数
Row(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(12),
),
child: Text(joke.category),
),
Spacer(),
Icon(Icons.thumb_up_outlined),
Text('${joke.likes}'),
],
),
// 标题
Text(
joke.title,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
// 内容预览
Text(
joke.content,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
// 操作按钮
Row(
children: [
TextButton.icon(
onPressed: () => onToggleFavorite(joke),
icon: Icon(joke.isFavorite ? Icons.favorite : Icons.favorite_border),
label: Text(joke.isFavorite ? '已收藏' : '收藏'),
),
TextButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: joke.content));
},
icon: Icon(Icons.copy),
label: Text('复制'),
),
],
),
],
),
),
),
);
}
卡片特点:
- 分类标签醒目
- 点赞数展示受欢迎程度
- 内容预览3行,超出显示省略号
- 收藏和复制按钮便捷操作
3. 随机笑话功能
使用Random类随机选择笑话。
class _RandomJokePageState extends State<RandomJokePage> {
late Joke _currentJoke;
final Random _random = Random();
void initState() {
super.initState();
_getRandomJoke();
}
void _getRandomJoke() {
setState(() {
_currentJoke = widget.jokes[_random.nextInt(widget.jokes.length)];
});
}
}
随机页面设计:
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange.shade300, Colors.orange.shade500],
),
),
child: Column(
children: [
// 头部
Text('随机笑话', style: TextStyle(color: Colors.white)),
// 笑话卡片
Card(
child: Column(
children: [
Text(joke.category),
Text(joke.title),
Text(joke.content),
],
),
),
// 换一个按钮
ElevatedButton.icon(
onPressed: _getRandomJoke,
icon: Icon(Icons.refresh),
label: Text('换一个'),
),
],
),
)
4. 收藏功能
使用列表管理收藏的笑话。
class _JokeHomePageState extends State<JokeHomePage> {
final List<Joke> _favorites = [];
void _toggleFavorite(Joke joke) {
setState(() {
joke.isFavorite = !joke.isFavorite;
if (joke.isFavorite) {
_favorites.add(joke);
} else {
_favorites.remove(joke);
}
});
}
}
收藏页面空状态:
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.favorite_border,
size: 80,
color: Colors.grey.shade300,
),
Text('还没有收藏的笑话'),
Text('快去收藏你喜欢的笑话吧'),
],
),
);
}
5. 复制分享功能
使用Clipboard API复制文本。
import 'package:flutter/services.dart';
void _copyJoke(Joke joke) {
Clipboard.setData(ClipboardData(
text: '${joke.title}\n\n${joke.content}',
));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已复制到剪贴板')),
);
}
分享流程:
- 用户点击复制按钮
- 将笑话内容复制到剪贴板
- 显示SnackBar提示
- 用户可以粘贴到其他应用分享
6. 笑话详情页面
class JokeDetailPage extends StatelessWidget {
final Joke joke;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('笑话详情'),
actions: [
IconButton(
icon: Icon(joke.isFavorite ? Icons.favorite : Icons.favorite_border),
onPressed: () => onToggleFavorite(joke),
),
IconButton(
icon: Icon(Icons.share),
onPressed: () => _shareJoke(joke),
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
// 渐变头部
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange.shade400, Colors.orange.shade600],
),
),
child: Column(
children: [
Text(joke.category),
Text(joke.title, style: TextStyle(color: Colors.white)),
],
),
),
// 内容区域
Padding(
padding: EdgeInsets.all(24),
child: Column(
children: [
Text(joke.content, style: TextStyle(height: 1.8)),
Row(
children: [
Icon(Icons.thumb_up_outlined),
Text('${joke.likes} 人觉得好笑'),
],
),
// 操作按钮
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildActionButton(Icons.thumb_up_outlined, '点赞'),
_buildActionButton(Icons.copy, '复制'),
_buildActionButton(Icons.share, '分享'),
],
),
],
),
),
],
),
),
);
}
}
操作按钮设计:
Widget _buildActionButton(IconData icon, String label, Color color) {
return Column(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(28),
),
child: Icon(icon, color: color, size: 28),
),
Text(label, style: TextStyle(fontSize: 12)),
],
);
}
UI组件设计
1. 渐变头部
Container(
padding: const EdgeInsets.fromLTRB(16, 48, 16, 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange.shade400, Colors.orange.shade600],
),
),
child: Row(
children: [
Icon(Icons.emoji_emotions, color: Colors.white, size: 32),
Column(
children: [
Text('笑话大全', style: TextStyle(color: Colors.white)),
Text('开心一刻,快乐每天', style: TextStyle(color: Colors.white70)),
],
),
Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(16),
),
child: Text('${jokes.length}个笑话'),
),
],
),
)
2. 分类标签
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(12),
),
child: Text(
category,
style: TextStyle(
fontSize: 12,
color: Colors.orange.shade700,
),
),
)
3. 卡片布局
Card(
margin: EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () {},
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 内容
],
),
),
),
)
4. NavigationBar底部导航
NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() {
_selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.list_outlined),
selectedIcon: Icon(Icons.list),
label: '笑话',
),
NavigationDestination(
icon: Icon(Icons.shuffle_outlined),
selectedIcon: Icon(Icons.shuffle),
label: '随机',
),
NavigationDestination(
icon: Icon(Icons.favorite_outline),
selectedIcon: Icon(Icons.favorite),
label: '收藏',
),
],
)
功能扩展建议
1. 接入笑话API
使用网络API获取更多笑话:
import 'package:http/http.dart' as http;
import 'dart:convert';
class JokeService {
static const String baseUrl = 'https://api.example.com/jokes';
Future<List<Joke>> getJokes({String? category}) async {
final url = category != null
? '$baseUrl?category=$category'
: baseUrl;
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
return data.map((json) => Joke.fromJson(json)).toList();
}
throw Exception('获取笑话失败');
}
Future<Joke> getRandomJoke() async {
final response = await http.get(Uri.parse('$baseUrl/random'));
if (response.statusCode == 200) {
return Joke.fromJson(json.decode(response.body));
}
throw Exception('获取随机笑话失败');
}
}
2. 数据持久化
使用SharedPreferences保存收藏:
import 'package:shared_preferences/shared_preferences.dart';
class StorageService {
static const String _favoritesKey = 'favorite_jokes';
Future<void> saveFavorites(List<Joke> favorites) async {
final prefs = await SharedPreferences.getInstance();
final jsonList = favorites.map((joke) => joke.toJson()).toList();
await prefs.setString(_favoritesKey, json.encode(jsonList));
}
Future<List<Joke>> loadFavorites() async {
final prefs = await SharedPreferences.getInstance();
final jsonStr = prefs.getString(_favoritesKey);
if (jsonStr == null) return [];
final List<dynamic> jsonList = json.decode(jsonStr);
return jsonList.map((json) => Joke.fromJson(json)).toList();
}
}
3. 搜索功能
添加笑话搜索:
class SearchPage extends StatefulWidget {
final List<Joke> jokes;
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
String _searchQuery = '';
List<Joke> get _searchResults {
if (_searchQuery.isEmpty) return widget.jokes;
return widget.jokes.where((joke) {
return joke.title.contains(_searchQuery) ||
joke.content.contains(_searchQuery);
}).toList();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
decoration: InputDecoration(
hintText: '搜索笑话...',
border: InputBorder.none,
),
onChanged: (value) {
setState(() {
_searchQuery = value;
});
},
),
),
body: ListView.builder(
itemCount: _searchResults.length,
itemBuilder: (context, index) {
return JokeCard(joke: _searchResults[index]);
},
),
);
}
}
4. 评论功能
添加用户评论:
class Comment {
final String username;
final String content;
final DateTime time;
Comment({
required this.username,
required this.content,
required this.time,
});
}
class CommentsSection extends StatelessWidget {
final List<Comment> comments;
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('评论 (${comments.length})'),
...comments.map((comment) {
return ListTile(
leading: CircleAvatar(
child: Text(comment.username[0]),
),
title: Text(comment.username),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(comment.content),
Text(
_formatTime(comment.time),
style: TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
);
}),
],
);
}
}
5. 分享到社交平台
使用share_plus包分享:
dependencies:
share_plus: ^7.2.2
import 'package:share_plus/share_plus.dart';
Future<void> shareJoke(Joke joke) async {
await Share.share(
'${joke.title}\n\n${joke.content}\n\n来自笑话大全App',
subject: joke.title,
);
}
// 分享图片
Future<void> shareJokeAsImage(Joke joke) async {
// 生成笑话图片
final image = await _generateJokeImage(joke);
await Share.shareXFiles(
[XFile(image.path)],
text: '${joke.title}\n\n来自笑话大全App',
);
}
6. 每日推荐
每天推荐一个笑话:
class DailyJokeService {
Future<Joke> getDailyJoke() async {
final prefs = await SharedPreferences.getInstance();
final today = DateTime.now().toString().substring(0, 10);
final lastDate = prefs.getString('daily_joke_date');
if (lastDate == today) {
// 返回今天已推荐的笑话
final jokeJson = prefs.getString('daily_joke');
if (jokeJson != null) {
return Joke.fromJson(json.decode(jokeJson));
}
}
// 生成新的每日笑话
final random = Random(DateTime.now().day);
final joke = jokes[random.nextInt(jokes.length)];
await prefs.setString('daily_joke_date', today);
await prefs.setString('daily_joke', json.encode(joke.toJson()));
return joke;
}
}
7. 笑话投稿
允许用户投稿笑话:
class SubmitJokePage extends StatefulWidget {
State<SubmitJokePage> createState() => _SubmitJokePageState();
}
class _SubmitJokePageState extends State<SubmitJokePage> {
final _titleController = TextEditingController();
final _contentController = TextEditingController();
String _selectedCategory = '冷笑话';
Future<void> _submitJoke() async {
final joke = {
'title': _titleController.text,
'content': _contentController.text,
'category': _selectedCategory,
};
final response = await http.post(
Uri.parse('https://api.example.com/jokes/submit'),
body: json.encode(joke),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('投稿成功,审核通过后将展示')),
);
Navigator.pop(context);
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('投稿笑话')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(labelText: '标题'),
),
DropdownButton<String>(
value: _selectedCategory,
items: categories.map((cat) {
return DropdownMenuItem(value: cat, child: Text(cat));
}).toList(),
onChanged: (value) {
setState(() {
_selectedCategory = value!;
});
},
),
TextField(
controller: _contentController,
decoration: InputDecoration(labelText: '内容'),
maxLines: 10,
),
ElevatedButton(
onPressed: _submitJoke,
child: Text('提交'),
),
],
),
),
);
}
}
8. 主题切换
支持多种主题:
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.light;
ThemeMode get themeMode => _themeMode;
void toggleTheme() {
_themeMode = _themeMode == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
notifyListeners();
}
}
// 在MaterialApp中使用
MaterialApp(
themeMode: themeProvider.themeMode,
theme: ThemeData.light().copyWith(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
),
darkTheme: ThemeData.dark().copyWith(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.orange,
brightness: Brightness.dark,
),
),
)
性能优化建议
1. 列表优化
使用ListView.builder按需构建:
ListView.builder(
itemCount: jokes.length,
itemBuilder: (context, index) {
return JokeCard(joke: jokes[index]);
},
)
2. 图片缓存
如果添加图片功能,使用缓存:
import 'package:cached_network_image/cached_network_image.dart';
CachedNetworkImage(
imageUrl: joke.imageUrl,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
3. 状态管理优化
对于大型应用,使用Provider:
class JokeProvider extends ChangeNotifier {
List<Joke> _jokes = [];
List<Joke> _favorites = [];
List<Joke> get jokes => _jokes;
List<Joke> get favorites => _favorites;
void toggleFavorite(Joke joke) {
joke.isFavorite = !joke.isFavorite;
if (joke.isFavorite) {
_favorites.add(joke);
} else {
_favorites.remove(joke);
}
notifyListeners();
}
}
4. 懒加载
分页加载笑话:
class _JokeListPageState extends State<JokeListPage> {
final ScrollController _scrollController = ScrollController();
int _currentPage = 1;
bool _isLoading = false;
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
}
void _onScroll() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_loadMore();
}
}
Future<void> _loadMore() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
final newJokes = await JokeService().getJokes(page: _currentPage + 1);
setState(() {
_jokes.addAll(newJokes);
_currentPage++;
_isLoading = false;
});
}
}
测试建议
1. 单元测试
测试笑话过滤逻辑:
void main() {
group('笑话过滤测试', () {
final jokes = [
Joke(id: 1, title: '笑话1', content: '内容1', category: '冷笑话'),
Joke(id: 2, title: '笑话2', content: '内容2', category: '脑筋急转弯'),
Joke(id: 3, title: '笑话3', content: '内容3', category: '冷笑话'),
];
test('全部分类应返回所有笑话', () {
final filtered = filterJokes(jokes, '全部');
expect(filtered.length, 3);
});
test('冷笑话分类应返回2个笑话', () {
final filtered = filterJokes(jokes, '冷笑话');
expect(filtered.length, 2);
expect(filtered.every((j) => j.category == '冷笑话'), true);
});
test('收藏功能测试', () {
final joke = jokes[0];
expect(joke.isFavorite, false);
toggleFavorite(joke);
expect(joke.isFavorite, true);
toggleFavorite(joke);
expect(joke.isFavorite, false);
});
});
}
2. Widget测试
测试UI组件:
void main() {
testWidgets('笑话卡片显示测试', (WidgetTester tester) async {
final joke = Joke(
id: 1,
title: '测试笑话',
content: '这是测试内容',
category: '冷笑话',
likes: 100,
);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: JokeCard(joke: joke),
),
),
);
expect(find.text('测试笑话'), findsOneWidget);
expect(find.text('冷笑话'), findsOneWidget);
expect(find.text('100'), findsOneWidget);
});
testWidgets('收藏按钮测试', (WidgetTester tester) async {
final joke = Joke(
id: 1,
title: '测试',
content: '内容',
category: '冷笑话',
);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: JokeCard(
joke: joke,
onToggleFavorite: (j) {
j.isFavorite = !j.isFavorite;
},
),
),
),
);
expect(find.byIcon(Icons.favorite_border), findsOneWidget);
await tester.tap(find.byIcon(Icons.favorite_border));
await tester.pump();
expect(joke.isFavorite, true);
});
}
3. 集成测试
测试完整流程:
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('完整笑话浏览流程', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
// 1. 验证初始显示
expect(find.text('笑话大全'), findsOneWidget);
expect(find.text('全部'), findsOneWidget);
// 2. 切换分类
await tester.tap(find.text('冷笑话'));
await tester.pumpAndSettle();
// 3. 点击笑话卡片
await tester.tap(find.byType(Card).first);
await tester.pumpAndSettle();
expect(find.text('笑话详情'), findsOneWidget);
// 4. 收藏笑话
await tester.tap(find.byIcon(Icons.favorite_border));
await tester.pumpAndSettle();
// 5. 返回并切换到收藏页面
await tester.pageBack();
await tester.pumpAndSettle();
await tester.tap(find.text('收藏'));
await tester.pumpAndSettle();
// 6. 验证收藏列表
expect(find.byType(Card), findsWidgets);
// 7. 切换到随机页面
await tester.tap(find.text('随机'));
await tester.pumpAndSettle();
expect(find.text('随机笑话'), findsOneWidget);
// 8. 点击换一个
await tester.tap(find.text('换一个'));
await tester.pumpAndSettle();
});
}
部署发布
1. Android打包
# 生成签名密钥
keytool -genkey -v -keystore ~/joke-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias joke
# 配置android/key.properties
storePassword=your_password
keyPassword=your_password
keyAlias=joke
storeFile=/path/to/joke-key.jks
# 构建APK
flutter build apk --release
# 构建App Bundle
flutter build appbundle --release
2. iOS打包
# 安装依赖
cd ios && pod install
# 构建IPA
flutter build ipa --release
3. 应用配置
在pubspec.yaml中配置:
name: joke_app
description: 笑话大全应用
version: 1.0.0+1
flutter:
uses-material-design: true
在AndroidManifest.xml中配置:
<application
android:label="笑话大全"
android:icon="@mipmap/ic_launcher">
项目总结
技术亮点
- 分类筛选:使用计算属性实现高效过滤
- 随机功能:Random类实现随机笑话
- 收藏管理:列表操作实现收藏功能
- 剪贴板操作:Clipboard API实现复制分享
- 渐变设计:LinearGradient提升视觉效果
- Material Design 3:使用最新的NavigationBar组件
- 卡片布局:Card和InkWell实现优雅交互
学习收获
通过本项目,你将掌握:
- 列表过滤和筛选
- 随机数生成
- 状态管理基础
- 剪贴板操作
- 页面导航
- 卡片布局设计
- 渐变效果实现
- 空状态处理
应用场景
本应用适合以下场景:
- 休闲娱乐:随时随地看笑话放松心情
- 社交分享:复制笑话分享给朋友
- 收藏管理:保存喜欢的笑话
- 学习案例:作为Flutter列表应用的学习项目
后续优化方向
- 网络数据:接入笑话API获取更多内容
- 数据持久化:保存收藏和浏览历史
- 搜索功能:支持关键词搜索笑话
- 评论互动:用户可以评论笑话
- 分享优化:支持分享到社交平台
- 每日推荐:每天推荐一个笑话
- 用户投稿:允许用户投稿笑话
- 主题切换:支持深色模式
代码规范
项目遵循以下规范:
- 使用const构造函数优化性能
- 组件拆分,职责单一
- 命名规范:驼峰命名法
- 注释清晰,代码可读性强
- 数据和UI分离
- 响应式布局设计
项目结构
lib/
├── main.dart # 主入口文件
├── models/ # 数据模型(可扩展)
│ └── joke.dart
├── pages/ # 页面组件(可扩展)
│ ├── joke_list_page.dart
│ ├── random_joke_page.dart
│ ├── favorites_page.dart
│ └── joke_detail_page.dart
├── widgets/ # 自定义组件(可扩展)
│ └── joke_card.dart
└── services/ # 业务逻辑(可扩展)
├── joke_service.dart
└── storage_service.dart
相关资源
推荐API:
- 笑话大全API:https://api.apiopen.top/api/getJoke
- 随机笑话API:https://v.api.aa1.cn/api/api-wenan-gaoxiao/index.php
推荐库:
http:网络请求share_plus:分享功能cached_network_image:图片缓存provider:状态管理
学习资源:
- Flutter官方文档:https://flutter.dev
- Material Design 3:https://m3.material.io
- Dart语言教程:https://dart.dev
本项目提供了完整的笑话应用功能,代码结构清晰,易于扩展。你可以在此基础上添加更多功能,打造一款功能丰富的娱乐应用。笑话内容可以根据需要替换或扩充,也可以接入真实的笑话API获取海量内容。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)