Flutter 框架跨平台鸿蒙开发 - 打造随机名言/毒鸡汤应用
/ 新类型case QuoteType.custom: return '自定义';// ...动画设计:FadeTransition淡入效果枚举扩展:为枚举添加属性和方法数据持久化:SharedPreferences + JSON序列化剪贴板操作分享功能:share_plus包Material 3设计:FilterChip、Badge等组件通过本项目,你不仅学会了如何实现名言应用,还掌握了Flut
·
Flutter实战:打造随机名言/毒鸡汤应用
前言
名言警句能给人启发和鼓励,而毒鸡汤则以幽默的方式调侃生活。本文将带你从零开始,使用Flutter开发一个功能完整的随机名言应用,支持多种类型、收藏、分享等功能。
应用特色
- 📚 六种名言类型:励志名言、毒鸡汤、哲理名言、搞笑段子、爱情语录、人生感悟
- 🎲 随机生成:点击按钮随机显示名言
- 🏷️ 类型筛选:按类型筛选名言
- ❤️ 收藏功能:收藏喜欢的名言
- 📋 复制功能:一键复制到剪贴板
- 📤 分享功能:分享到社交平台
- ✨ 淡入动画:切换名言时的平滑过渡
- 💾 数据持久化:收藏数据本地保存
- 🎨 渐变设计:每种类型都有独特配色
- 🌓 深色模式:自动适配系统主题
效果展示


数据模型设计
1. 名言类型枚举
enum QuoteType {
motivational, // 励志名言
poisonous, // 毒鸡汤
philosophical, // 哲理名言
funny, // 搞笑段子
love, // 爱情语录
life; // 人生感悟
String get label {
switch (this) {
case QuoteType.motivational: return '励志名言';
case QuoteType.poisonous: return '毒鸡汤';
// ...
}
}
IconData get icon {
switch (this) {
case QuoteType.motivational: return Icons.trending_up;
case QuoteType.poisonous: return Icons.local_drink;
// ...
}
}
Color get color {
switch (this) {
case QuoteType.motivational: return Colors.orange;
case QuoteType.poisonous: return Colors.green;
// ...
}
}
}
2. 名言模型
class Quote {
final String text; // 名言文本
final String? author; // 作者(可选)
final QuoteType type; // 类型
bool isFavorite; // 是否收藏
Quote({
required this.text,
this.author,
required this.type,
this.isFavorite = false,
});
// JSON序列化
Map<String, dynamic> toJson() => {
'text': text,
'author': author,
'type': type.index,
'isFavorite': isFavorite,
};
// JSON反序列化
factory Quote.fromJson(Map<String, dynamic> json) => Quote(
text: json['text'],
author: json['author'],
type: QuoteType.values[json['type']],
isFavorite: json['isFavorite'] ?? false,
);
}
名言数据库
1. 励志名言
static final List<Map<String, String>> motivationalQuotes = [
{
'text': '成功不是终点,失败也不是终结,唯有勇气才是永恒。',
'author': '温斯顿·丘吉尔'
},
{
'text': '你的时间有限,不要浪费在重复他人的生活上。',
'author': '史蒂夫·乔布斯'
},
{
'text': '世界上只有一种真正的英雄主义,那就是认清生活的真相后依然热爱它。',
'author': '罗曼·罗兰'
},
// ... 更多名言
];
2. 毒鸡汤
static final List<Map<String, String>> poisonousQuotes = [
{'text': '你只是看起来很努力,实际上只是效率低下。', 'author': ''},
{'text': '比你优秀的人还在努力,你有什么资格不努力?但你努力了也没用。', 'author': ''},
{'text': '失败并不可怕,可怕的是你还相信这句话。', 'author': ''},
{'text': '你以为有钱人很快乐吗?他们的快乐你根本想象不到。', 'author': ''},
{'text': '条条大路通罗马,而有些人就住在罗马。', 'author': ''},
// ... 更多毒鸡汤
];
3. 随机获取名言
static Quote getRandomQuote([QuoteType? type]) {
final random = Random();
if (type != null) {
// 获取指定类型的名言
final quotes = getQuotesByType(type);
return quotes[random.nextInt(quotes.length)];
} else {
// 随机选择类型
final allTypes = QuoteType.values;
final randomType = allTypes[random.nextInt(allTypes.length)];
return getRandomQuote(randomType);
}
}
核心功能实现
1. 淡入动画
class _QuotePageState extends State<QuotePage>
with TickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _fadeController, curve: Curves.easeIn),
);
}
void _generateNewQuote() {
setState(() {
_currentQuote = QuoteDatabase.getRandomQuote(_selectedType);
});
// 重置并播放动画
_fadeController.reset();
_fadeController.forward();
}
}
2. 名言卡片
Widget _buildQuoteCard() {
return FadeTransition(
opacity: _fadeAnimation,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
_currentQuote!.type.color.withOpacity(0.1),
_currentQuote!.type.color.withOpacity(0.05),
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: _currentQuote!.type.color.withOpacity(0.3),
width: 2,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 类型标签
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: _currentQuote!.type.color.withOpacity(0.2),
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(_currentQuote!.type.icon, size: 16),
const SizedBox(width: 6),
Text(_currentQuote!.type.label),
],
),
),
const SizedBox(height: 20),
// 引号图标
Icon(
Icons.format_quote,
size: 32,
color: _currentQuote!.type.color.withOpacity(0.5),
),
const SizedBox(height: 12),
// 名言文本
Text(
_currentQuote!.text,
style: const TextStyle(
fontSize: 20,
height: 1.6,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
// 作者
if (_currentQuote!.author != null)
Align(
alignment: Alignment.centerRight,
child: Text(
'—— ${_currentQuote!.author}',
style: TextStyle(
fontSize: 14,
fontStyle: FontStyle.italic,
color: Colors.grey.shade600,
),
),
),
],
),
),
);
}
3. 收藏功能
void _toggleFavorite() {
if (_currentQuote == null) return;
setState(() {
_currentQuote!.isFavorite = !_currentQuote!.isFavorite;
if (_currentQuote!.isFavorite) {
_favorites.add(_currentQuote!);
} else {
_favorites.removeWhere((q) => q.text == _currentQuote!.text);
}
});
_saveFavorites();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
_currentQuote!.isFavorite ? '已添加到收藏' : '已取消收藏'
),
duration: const Duration(seconds: 1),
),
);
}
Future<void> _saveFavorites() async {
final prefs = await SharedPreferences.getInstance();
final String encoded = json.encode(
_favorites.map((q) => q.toJson()).toList()
);
await prefs.setString('favorites', encoded);
}
4. 复制到剪贴板
void _copyToClipboard() {
if (_currentQuote == null) return;
final text = _currentQuote!.author != null
? '${_currentQuote!.text}\n\n—— ${_currentQuote!.author}'
: _currentQuote!.text;
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('已复制到剪贴板'),
duration: Duration(seconds: 1),
),
);
}
5. 分享功能
void _shareQuote() {
if (_currentQuote == null) return;
final text = _currentQuote!.author != null
? '${_currentQuote!.text}\n\n—— ${_currentQuote!.author}'
: _currentQuote!.text;
Share.share(text);
}
6. 类型筛选
Widget _buildTypeFilter() {
return SizedBox(
height: 50,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
children: [
// 全部选项
FilterChip(
label: const Text('全部'),
selected: _selectedType == null,
onSelected: (selected) {
setState(() => _selectedType = null);
_generateNewQuote();
},
),
// 各类型选项
...QuoteType.values.map((type) {
return FilterChip(
avatar: Icon(type.icon, size: 18),
label: Text(type.label),
selected: _selectedType == type,
onSelected: (selected) {
setState(() {
_selectedType = selected ? type : null;
});
_generateNewQuote();
},
);
}),
],
),
);
}
UI组件设计
1. 操作按钮
Widget _buildActionButtons() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildActionButton(
icon: _currentQuote?.isFavorite == true
? Icons.favorite
: Icons.favorite_border,
label: '收藏',
color: Colors.red,
onPressed: _toggleFavorite,
),
_buildActionButton(
icon: Icons.copy,
label: '复制',
color: Colors.blue,
onPressed: _copyToClipboard,
),
_buildActionButton(
icon: Icons.share,
label: '分享',
color: Colors.green,
onPressed: _shareQuote,
),
],
);
}
Widget _buildActionButton({
required IconData icon,
required String label,
required Color color,
required VoidCallback onPressed,
}) {
return Column(
children: [
IconButton.filled(
onPressed: onPressed,
icon: Icon(icon),
style: IconButton.styleFrom(
backgroundColor: color.withOpacity(0.2),
foregroundColor: color,
padding: const EdgeInsets.all(16),
),
),
const SizedBox(height: 8),
Text(
label,
style: TextStyle(
fontSize: 12,
color: color,
fontWeight: FontWeight.w500,
),
),
],
);
}
2. 收藏夹页面
class FavoritesPage extends StatelessWidget {
final List<Quote> favorites;
final Function(Quote) onDelete;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('收藏夹')),
body: favorites.isEmpty
? _buildEmptyState()
: ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
return _buildFavoriteItem(context, favorites[index]);
},
),
);
}
Widget _buildFavoriteItem(BuildContext context, Quote quote) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// 类型标签
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
decoration: BoxDecoration(
color: quote.type.color.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(quote.type.icon, size: 14),
const SizedBox(width: 4),
Text(quote.type.label),
],
),
),
const Spacer(),
// 删除按钮
IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () => _showDeleteDialog(context, quote),
),
],
),
const SizedBox(height: 12),
Text(quote.text),
if (quote.author != null)
Text('—— ${quote.author}'),
],
),
),
);
}
}
3. Badge徽章
显示收藏数量:
IconButton(
icon: Badge(
label: Text(_favorites.length.toString()),
isLabelVisible: _favorites.isNotEmpty,
child: const Icon(Icons.favorite),
),
onPressed: () {
// 打开收藏夹
},
)
技术要点详解
1. FadeTransition动画
| 属性 | 说明 |
|---|---|
| opacity | 透明度动画 |
| child | 子组件 |
| duration | 动画时长 |
| curve | 动画曲线 |
FadeTransition(
opacity: _fadeAnimation,
child: Widget(),
)
2. Clipboard剪贴板操作
// 复制到剪贴板
Clipboard.setData(ClipboardData(text: 'Hello'));
// 从剪贴板读取
ClipboardData? data = await Clipboard.getData('text/plain');
String? text = data?.text;
3. Share分享功能
// 分享文本
Share.share('分享内容');
// 分享文件
Share.shareXFiles([XFile('path/to/file')]);
// 分享带标题
Share.share('内容', subject: '标题');
4. FilterChip筛选器
FilterChip(
label: Text('标签'),
avatar: Icon(Icons.star), // 前置图标
selected: isSelected, // 是否选中
onSelected: (bool selected) { // 选中回调
// 处理选中状态
},
)
功能扩展建议
1. 每日一句
每天推送一条名言:
class DailyQuote {
static Future<Quote> getTodayQuote() async {
final prefs = await SharedPreferences.getInstance();
final today = DateTime.now().toIso8601String().split('T')[0];
final savedDate = prefs.getString('daily_quote_date');
if (savedDate != today) {
// 生成新的每日名言
final quote = QuoteDatabase.getRandomQuote();
await prefs.setString('daily_quote_date', today);
await prefs.setString('daily_quote', json.encode(quote.toJson()));
return quote;
} else {
// 返回今天的名言
final quoteJson = prefs.getString('daily_quote');
return Quote.fromJson(json.decode(quoteJson!));
}
}
}
2. 本地通知
定时推送名言:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationService {
static final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
static Future<void> scheduleDailyQuote() async {
final quote = QuoteDatabase.getRandomQuote();
await _notifications.zonedSchedule(
0,
'每日名言',
quote.text,
_nextInstanceOf9AM(),
const NotificationDetails(
android: AndroidNotificationDetails(
'daily_quote',
'每日名言',
importance: Importance.high,
),
),
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
}
3. 主题背景
为不同类型设置不同背景:
class QuoteBackground extends StatelessWidget {
final QuoteType type;
final Widget child;
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/bg_${type.name}.jpg'),
fit: BoxFit.cover,
opacity: 0.3,
),
),
child: child,
);
}
}
4. 语音朗读
使用TTS朗读名言:
import 'package:flutter_tts/flutter_tts.dart';
class TTSService {
static final FlutterTts _tts = FlutterTts();
static Future<void> speak(String text) async {
await _tts.setLanguage('zh-CN');
await _tts.setPitch(1.0);
await _tts.setSpeechRate(0.5);
await _tts.speak(text);
}
static Future<void> stop() async {
await _tts.stop();
}
}
5. 图片生成
将名言生成为图片:
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
Future<Uint8List> generateQuoteImage(Quote quote) async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
final size = const Size(800, 600);
// 绘制背景
final paint = Paint()..color = quote.type.color.withOpacity(0.1);
canvas.drawRect(Offset.zero & size, paint);
// 绘制文本
final textPainter = TextPainter(
text: TextSpan(
text: quote.text,
style: const TextStyle(fontSize: 24, color: Colors.black),
),
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: size.width - 40);
textPainter.paint(canvas, const Offset(20, 100));
// 转换为图片
final picture = recorder.endRecording();
final image = await picture.toImage(
size.width.toInt(),
size.height.toInt(),
);
final byteData = await image.toByteData(
format: ui.ImageByteFormat.png,
);
return byteData!.buffer.asUint8List();
}
6. 搜索功能
List<Quote> searchQuotes(String query) {
final allQuotes = <Quote>[];
for (var type in QuoteType.values) {
allQuotes.addAll(QuoteDatabase.getQuotesByType(type));
}
return allQuotes.where((quote) {
return quote.text.toLowerCase().contains(query.toLowerCase()) ||
(quote.author?.toLowerCase().contains(query.toLowerCase()) ?? false);
}).toList();
}
性能优化建议
1. 名言预加载
class QuoteCache {
static final Map<QuoteType, List<Quote>> _cache = {};
static void preload() {
for (var type in QuoteType.values) {
_cache[type] = QuoteDatabase.getQuotesByType(type);
}
}
static Quote getRandomQuote(QuoteType type) {
final quotes = _cache[type]!;
return quotes[Random().nextInt(quotes.length)];
}
}
2. 收藏夹分页
class _FavoritesPageState extends State<FavoritesPage> {
int _displayCount = 20;
final ScrollController _scrollController = ScrollController();
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
}
void _onScroll() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
_displayCount += 20;
});
}
}
List<Quote> get _displayedFavorites {
return widget.favorites.take(_displayCount).toList();
}
}
常见问题解答
Q1: 如何添加更多名言?
A: 在QuoteDatabase类中添加新的名言数据:
static final List<Map<String, String>> motivationalQuotes = [
// 现有名言
{'text': '新的名言', 'author': '作者'},
];
Q2: 如何自定义名言类型?
A: 在QuoteType枚举中添加新类型:
enum QuoteType {
motivational,
poisonous,
custom; // 新类型
String get label {
switch (this) {
case QuoteType.custom: return '自定义';
// ...
}
}
}
Q3: 如何实现名言历史记录?
A: 添加历史记录功能:
class QuoteHistory {
static final List<Quote> _history = [];
static void add(Quote quote) {
_history.insert(0, quote);
if (_history.length > 100) {
_history.removeLast();
}
}
static List<Quote> getHistory() => _history;
}
项目结构
lib/
├── main.dart # 主程序入口
├── models/
│ ├── quote_type.dart # 名言类型枚举
│ └── quote.dart # 名言模型
├── data/
│ └── quote_database.dart # 名言数据库
├── screens/
│ ├── quote_page.dart # 主页面
│ └── favorites_page.dart # 收藏夹页面
├── widgets/
│ ├── quote_card.dart # 名言卡片
│ ├── type_filter.dart # 类型筛选器
│ └── action_buttons.dart # 操作按钮
└── services/
├── storage_service.dart # 存储服务
└── share_service.dart # 分享服务
总结
本文实现了一个功能完整的随机名言应用,涵盖了以下核心技术:
- 动画设计:FadeTransition淡入效果
- 枚举扩展:为枚举添加属性和方法
- 数据持久化:SharedPreferences + JSON序列化
- 剪贴板操作:Clipboard API
- 分享功能:share_plus包
- Material 3设计:FilterChip、Badge等组件
通过本项目,你不仅学会了如何实现名言应用,还掌握了Flutter中动画、数据管理、系统交互的核心技术。这些知识可以应用到更多场景,如每日一句、心灵鸡汤、语录分享等领域。
名言虽短,却能给人启发。希望这个应用能为用户带来正能量和思考!
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)