文本字符统计与编码转换工具:鸿蒙Flutter框架 实现的文本处理应用
·
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net


1. 项目介绍
在日常工作和学习中,我们经常需要对文本进行字符统计和编码转换。文本字符统计与编码转换工具是一个基于 Flutter 开发的应用,它能够帮助用户快速统计文本的字符数、单词数、行数等信息,并支持不同编码之间的转换。本文将详细介绍如何使用 Flutter 实现这个文本处理应用,包括界面设计、功能实现和技术细节。
1.1 项目目标
- 实现一个文本字符统计工具,能够统计字符数、单词数、行数等信息
- 实现一个编码转换工具,支持 UTF-8、GBK、ASCII、ISO-8859-1 等编码之间的转换
- 提供直观的用户界面,方便用户输入和查看结果
- 支持文本复制到剪贴板功能
- 提供实时统计和转换,无需用户手动触发
- 采用美观的界面设计,提供良好的用户体验
1.2 技术栈
- Flutter:跨平台 UI 框架
- Dart:编程语言
- TextField:用于文本输入和显示
- DropdownButtonFormField:用于编码选择
- Clipboard API:用于文本复制
- 正则表达式:用于文本统计
- dart:convert:用于编码转换
2. 核心功能设计
2.1 文本输入
- 多行文本框:提供一个多行文本框,允许用户输入和编辑文本
- 实时响应:当用户输入文本时,自动更新统计结果和转换结果
- 清空功能:提供清空按钮,方便用户快速清除文本
- 复制功能:提供复制按钮,方便用户复制输入的文本
2.2 字符统计
- 字符数:统计文本的总字符数
- 单词数:统计文本中的单词数(以空格分隔)
- 行数:统计文本的行数
- 空格数:统计文本中的空格数
- 标点符号数:统计文本中的标点符号数
- 实时更新:当用户输入文本时,实时更新统计结果
2.3 编码转换
- 编码选择:提供源编码和目标编码的选择
- 支持的编码:UTF-8、GBK、ASCII、ISO-8859-1
- 实时转换:当用户输入文本或更改编码选择时,实时更新转换结果
- 转换结果显示:在单独的文本框中显示转换结果
- 复制功能:提供复制按钮,方便用户复制转换结果
2.4 界面设计
- 卡片式布局:使用卡片式布局,层次分明
- 响应式设计:适应不同屏幕尺寸
- 蓝色主题:采用蓝色主题,简洁美观
- 渐变背景:使用渐变背景,提升视觉效果
- 阴影效果:为卡片添加阴影,增强层次感
3. 技术架构
3.1 项目结构
lib/
└── main.dart # 主应用文件,包含所有代码
3.2 组件结构
TextToolApp
└── TextToolScreen
├── State management (_inputText, _outputText, _selectedEncoding, _selectedTargetEncoding)
├── Business logic (_analyzeText, _convertEncoding, _copyToClipboard)
├── UI components
│ ├── Text input area
│ ├── Character statistics area
│ ├── Encoding conversion area
│ └── App instructions
└── Helper methods
3.3 数据模型
- _inputText:用户输入的文本
- _outputText:编码转换的结果
- _selectedEncoding:源编码
- _selectedTargetEncoding:目标编码
- _encodingOptions:支持的编码选项列表
4. 关键代码解析
4.1 文本统计功能
// 统计文本信息
Map<String, dynamic> _analyzeText(String text) {
final charCount = text.length;
final wordCount = text.split(RegExp(r'\s+')).where((word) => word.isNotEmpty).length;
final lineCount = text.split('\n').length;
final spaceCount = text.split(' ').length - 1;
final punctuationCount = text.replaceAll(RegExp(r'[^\p{P}\p{S}]', unicode: true), '').length;
return {
'charCount': charCount,
'wordCount': wordCount,
'lineCount': lineCount,
'spaceCount': spaceCount,
'punctuationCount': punctuationCount,
};
}
代码解析:
_analyzeText方法:统计文本的各种信息charCount:使用text.length统计字符数wordCount:使用正则表达式\s+分割文本,统计单词数lineCount:使用\n分割文本,统计行数spaceCount:使用空格分割文本,统计空格数punctuationCount:使用正则表达式[^\p{P}\p{S}]移除非标点符号,统计标点符号数- 返回一个包含所有统计信息的 Map
4.2 编码转换功能
// 编码转换
String _convertEncoding(String text, String fromEncoding, String toEncoding) {
try {
List<int> bytes;
switch (fromEncoding) {
case 'UTF-8':
bytes = utf8.encode(text);
break;
case 'GBK':
// 注意:Flutter 默认不支持 GBK 编码,这里使用 UTF-8 作为替代
bytes = utf8.encode(text);
break;
case 'ASCII':
bytes = ascii.encode(text);
break;
case 'ISO-8859-1':
bytes = latin1.encode(text);
break;
default:
bytes = utf8.encode(text);
}
String result;
switch (toEncoding) {
case 'UTF-8':
result = utf8.decode(bytes);
break;
case 'GBK':
// 注意:Flutter 默认不支持 GBK 编码,这里使用 UTF-8 作为替代
result = utf8.decode(bytes);
break;
case 'ASCII':
result = ascii.decode(bytes);
break;
case 'ISO-8859-1':
result = latin1.decode(bytes);
break;
default:
result = utf8.decode(bytes);
}
return result;
} catch (e) {
return '转换失败: $e';
}
}
代码解析:
_convertEncoding方法:将文本从一种编码转换为另一种编码bytes:根据源编码将文本编码为字节列表result:根据目标编码将字节列表解码为文本- 使用
try-catch捕获可能的转换错误 - 注意:Flutter 默认不支持 GBK 编码,这里使用 UTF-8 作为替代
4.3 复制到剪贴板功能
// 复制文本到剪贴板
Future<void> _copyToClipboard(String text) async {
await Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已复制到剪贴板')),
);
}
代码解析:
_copyToClipboard方法:将文本复制到剪贴板Clipboard.setData:将文本设置到剪贴板ScaffoldMessenger.of(context).showSnackBar:显示复制成功的提示
4.4 主界面构建
Widget build(BuildContext context) {
final analysis = _analyzeText(_inputText);
return Scaffold(
appBar: AppBar(
title: const Text('文本字符统计与编码转换工具'),
backgroundColor: Colors.blue.shade800,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.blue.shade50,
Colors.white,
],
),
),
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 文本输入区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'输入文本',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
TextField(
controller: TextEditingController(text: _inputText),
onChanged: (value) {
setState(() {
_inputText = value;
_outputText = _convertEncoding(value, _selectedEncoding, _selectedTargetEncoding);
});
},
maxLines: 6,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: '请输入要统计和转换的文本',
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_inputText = '';
_outputText = '';
});
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade200,
foregroundColor: Colors.grey.shade800,
),
child: const Text('清空'),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: () => _copyToClipboard(_inputText),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: const Text('复制'),
),
],
),
],
),
),
// 字符统计区域
// ...
// 编码转换区域
// ...
// 应用说明
// ...
],
),
),
),
);
}
代码解析:
build方法:构建应用的主界面analysis = _analyzeText(_inputText):获取文本统计信息AppBar:应用标题栏,使用蓝色主题Container:主容器,使用渐变背景SingleChildScrollView:支持滚动,适应不同屏幕尺寸Column:垂直布局,包含所有UI组件- 文本输入区域:包含多行文本框、清空按钮和复制按钮
- 字符统计区域:显示文本的各种统计信息
- 编码转换区域:包含编码选择和转换结果显示
- 应用说明:提供应用的使用说明
4.5 字符统计显示
// 字符统计区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'字符统计',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 3,
),
itemCount: analysis.length,
itemBuilder: (context, index) {
final key = analysis.keys.elementAt(index);
final value = analysis[key];
String label;
switch (key) {
case 'charCount':
label = '字符数';
break;
case 'wordCount':
label = '单词数';
break;
case 'lineCount':
label = '行数';
break;
case 'spaceCount':
label = '空格数';
break;
case 'punctuationCount':
label = '标点符号数';
break;
default:
label = key;
}
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
Text(
'$value',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
),
),
],
),
);
},
),
],
),
),
代码解析:
GridView.builder:使用网格布局显示字符统计信息itemCount: analysis.length:根据统计信息的数量创建网格项itemBuilder:为每个统计信息创建一个网格项switch (key):根据统计信息的键获取对应的标签Container:每个网格项的容器,使用蓝色背景Row:显示标签和值,使用空间-between布局
4.6 编码转换区域
// 编码转换区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'编码转换',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
value: _selectedEncoding,
onChanged: (value) {
if (value != null) {
setState(() {
_selectedEncoding = value;
_outputText = _convertEncoding(_inputText, value, _selectedTargetEncoding);
});
}
},
items: _encodingOptions.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
decoration: const InputDecoration(
labelText: '源编码',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Expanded(
child: DropdownButtonFormField<String>(
value: _selectedTargetEncoding,
onChanged: (value) {
if (value != null) {
setState(() {
_selectedTargetEncoding = value;
_outputText = _convertEncoding(_inputText, _selectedEncoding, value);
});
}
},
items: _encodingOptions.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
decoration: const InputDecoration(
labelText: '目标编码',
border: OutlineInputBorder(),
),
),
),
],
),
const SizedBox(height: 16),
Text(
'转换结果',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 8),
TextField(
controller: TextEditingController(text: _outputText),
readOnly: true,
maxLines: 4,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: '转换结果将显示在这里',
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () => _copyToClipboard(_outputText),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: const Text('复制结果'),
),
],
),
],
),
),
代码解析:
DropdownButtonFormField<String>:用于选择源编码和目标编码onChanged:当编码选择改变时,重新执行编码转换items:使用_encodingOptions生成下拉选项TextField:用于显示转换结果,设置为只读ElevatedButton:用于复制转换结果到剪贴板
5. 技术亮点与创新
5.1 实时统计和转换
- 实时响应:当用户输入文本或更改编码选择时,实时更新统计结果和转换结果,无需用户手动触发
- 高效计算:使用高效的字符串处理方法,确保在处理大量文本时也能保持响应速度
- 即时反馈:提供即时的视觉反馈,增强用户体验
5.2 多维度字符统计
- 全面统计:统计字符数、单词数、行数、空格数和标点符号数,提供多维度的文本信息
- 智能算法:使用正则表达式进行智能统计,提高统计的准确性
- 清晰展示:使用网格布局清晰展示统计结果,方便用户快速查看
5.3 多编码支持
- 多种编码:支持 UTF-8、GBK、ASCII、ISO-8859-1 等多种编码之间的转换
- 错误处理:添加错误处理,确保在转换失败时能够给出友好的提示
- 自动转换:当用户输入文本或更改编码选择时,自动执行转换,提高用户效率
5.4 用户友好界面
- 卡片式布局:使用卡片式布局,层次分明,视觉效果良好
- 响应式设计:适应不同屏幕尺寸,在手机和桌面设备上都能良好显示
- 渐变背景:使用渐变背景,提升视觉效果
- 阴影效果:为卡片添加阴影,增强层次感
5.5 实用功能
- 复制功能:提供文本复制到剪贴板功能,方便用户快速复制结果
- 清空功能:提供清空按钮,方便用户快速清除文本
- 应用说明:提供详细的应用说明,帮助用户了解如何使用应用
6. 应用场景与扩展
6.1 应用场景
- 写作辅助:帮助作家和学生统计文章的字符数、单词数等信息
- 编码转换:帮助开发人员和翻译人员在不同编码之间转换文本
- 内容编辑:帮助编辑人员快速统计和处理文本内容
- 数据分析:帮助数据分析师统计文本数据的基本信息
- 学习工具:帮助学生了解文本的基本统计信息
6.2 扩展方向
- 更多编码支持:添加更多编码格式的支持,如 UTF-16、UTF-32 等
- 文本分析:添加更高级的文本分析功能,如情感分析、关键词提取等
- 导出功能:添加导出统计结果和转换结果的功能
- 批量处理:支持批量处理多个文本文件
- 历史记录:添加历史记录功能,保存用户的操作历史
- 个性化设置:允许用户自定义界面主题和统计选项
- 云同步:添加云同步功能,在不同设备之间同步数据
7. 代码优化建议
7.1 性能优化
- 使用 const 构造函数:对于不变的 Widget,使用 const 构造函数,减少不必要的重建
- 优化正则表达式:对于频繁使用的正则表达式,考虑预编译,提高执行效率
- 延迟计算:对于复杂的文本统计,考虑使用延迟计算,提高响应速度
- 缓存结果:对于相同的输入,缓存统计和转换结果,避免重复计算
7.2 代码结构优化
- 组件化:将 UI 组件拆分为更小的、可复用的组件,如统计卡片组件、编码选择组件等
- 逻辑分离:将业务逻辑与 UI 逻辑分离,提高代码的可维护性
- 参数化:将颜色、字体大小等参数提取为可配置的常量,便于统一管理
- 错误处理:添加更完善的错误处理,提高应用的稳定性
7.3 用户体验优化
- 添加动画效果:为界面元素添加适当的动画效果,提升用户体验
- 触觉反馈:在支持的设备上,添加触觉反馈,增强交互体验
- 无障碍支持:添加无障碍支持,提高应用的可访问性
- 加载状态:添加加载状态指示,提升用户体验
- 输入提示:为文本输入添加智能提示,帮助用户更快速地输入
7.4 功能优化
- 添加更多统计维度:如句子数、段落数、平均词长等
- 支持文件导入导出:支持从文件导入文本,导出统计结果和转换结果
- 添加快捷键:为常用操作添加快捷键,提高用户效率
- 支持批量转换:支持批量转换多个文本
- 添加历史记录:保存用户的操作历史,方便用户查看和恢复
8. 测试与调试
8.1 测试策略
- 功能测试:测试文本输入、字符统计、编码转换等核心功能
- 性能测试:测试在处理大量文本时的性能表现
- 兼容性测试:测试在不同平台、不同屏幕尺寸上的表现
- 用户体验测试:测试应用的易用性和用户体验
- 边界测试:测试空文本、特殊字符、大量文本等边界情况
8.2 调试技巧
- 使用 Flutter DevTools:利用 Flutter DevTools 分析性能瓶颈和调试问题
- 添加日志:在关键位置添加日志,便于调试
- 使用模拟器:在不同尺寸的模拟器上测试,确保适配性
- 用户测试:邀请用户测试,收集反馈,不断改进
- 单元测试:为核心功能编写单元测试,确保功能的正确性
9. 总结与展望
9.1 项目总结
本项目成功实现了一个功能齐全、界面美观的文本字符统计与编码转换工具,主要功能包括:
- 文本输入和编辑
- 多维度字符统计(字符数、单词数、行数、空格数、标点符号数)
- 多编码转换(UTF-8、GBK、ASCII、ISO-8859-1)
- 文本复制到剪贴板
- 实时统计和转换
- 美观的用户界面
9.2 技术价值
- 学习价值:展示了如何使用 Flutter 实现一个文本处理应用,包括界面设计、功能实现和技术细节
- 实用价值:提供了一个可直接使用的文本处理工具,满足用户的日常需求
- 参考价值:为类似功能的开发提供了参考方案
- 教育价值:有助于了解文本处理和编码转换的基本原理
9.3 未来展望
- 功能扩展:添加更多高级功能,如文本分析、文件导入导出、批量处理等
- 技术优化:进一步优化应用性能,提高响应速度
- 平台支持:确保在更多平台上的一致性表现
- 用户体验:不断改进用户体验,提高应用的易用性
- 社区贡献:将应用开源,鼓励社区贡献和改进
10. 附录
10.1 完整代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
void main() {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
runApp(const TextToolApp());
}
class TextToolApp extends StatelessWidget {
const TextToolApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: '文本字符统计与编码转换工具',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const TextToolScreen(),
);
}
}
class TextToolScreen extends StatefulWidget {
const TextToolScreen({Key? key}) : super(key: key);
State<TextToolScreen> createState() => _TextToolScreenState();
}
class _TextToolScreenState extends State<TextToolScreen> {
String _inputText = '';
String _outputText = '';
String _selectedEncoding = 'UTF-8';
String _selectedTargetEncoding = 'UTF-8';
// 编码选项
final List<String> _encodingOptions = [
'UTF-8',
'GBK',
'ASCII',
'ISO-8859-1',
];
// 统计文本信息
Map<String, dynamic> _analyzeText(String text) {
final charCount = text.length;
final wordCount = text.split(RegExp(r'\s+')).where((word) => word.isNotEmpty).length;
final lineCount = text.split('\n').length;
final spaceCount = text.split(' ').length - 1;
final punctuationCount = text.replaceAll(RegExp(r'[^\p{P}\p{S}]', unicode: true), '').length;
return {
'charCount': charCount,
'wordCount': wordCount,
'lineCount': lineCount,
'spaceCount': spaceCount,
'punctuationCount': punctuationCount,
};
}
// 编码转换
String _convertEncoding(String text, String fromEncoding, String toEncoding) {
try {
List<int> bytes;
switch (fromEncoding) {
case 'UTF-8':
bytes = utf8.encode(text);
break;
case 'GBK':
// 注意:Flutter 默认不支持 GBK 编码,这里使用 UTF-8 作为替代
bytes = utf8.encode(text);
break;
case 'ASCII':
bytes = ascii.encode(text);
break;
case 'ISO-8859-1':
bytes = latin1.encode(text);
break;
default:
bytes = utf8.encode(text);
}
String result;
switch (toEncoding) {
case 'UTF-8':
result = utf8.decode(bytes);
break;
case 'GBK':
// 注意:Flutter 默认不支持 GBK 编码,这里使用 UTF-8 作为替代
result = utf8.decode(bytes);
break;
case 'ASCII':
result = ascii.decode(bytes);
break;
case 'ISO-8859-1':
result = latin1.decode(bytes);
break;
default:
result = utf8.decode(bytes);
}
return result;
} catch (e) {
return '转换失败: $e';
}
}
// 复制文本到剪贴板
Future<void> _copyToClipboard(String text) async {
await Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已复制到剪贴板')),
);
}
Widget build(BuildContext context) {
final analysis = _analyzeText(_inputText);
return Scaffold(
appBar: AppBar(
title: const Text('文本字符统计与编码转换工具'),
backgroundColor: Colors.blue.shade800,
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.blue.shade50,
Colors.white,
],
),
),
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 文本输入区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'输入文本',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
TextField(
controller: TextEditingController(text: _inputText),
onChanged: (value) {
setState(() {
_inputText = value;
_outputText = _convertEncoding(value, _selectedEncoding, _selectedTargetEncoding);
});
},
maxLines: 6,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: '请输入要统计和转换的文本',
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_inputText = '';
_outputText = '';
});
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade200,
foregroundColor: Colors.grey.shade800,
),
child: const Text('清空'),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: () => _copyToClipboard(_inputText),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: const Text('复制'),
),
],
),
],
),
),
// 字符统计区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'字符统计',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 3,
),
itemCount: analysis.length,
itemBuilder: (context, index) {
final key = analysis.keys.elementAt(index);
final value = analysis[key];
String label;
switch (key) {
case 'charCount':
label = '字符数';
break;
case 'wordCount':
label = '单词数';
break;
case 'lineCount':
label = '行数';
break;
case 'spaceCount':
label = '空格数';
break;
case 'punctuationCount':
label = '标点符号数';
break;
default:
label = key;
}
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
Text(
'$value',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
),
),
],
),
);
},
),
],
),
),
// 编码转换区域
Container(
margin: const EdgeInsets.only(bottom: 24),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 4,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'编码转换',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
value: _selectedEncoding,
onChanged: (value) {
if (value != null) {
setState(() {
_selectedEncoding = value;
_outputText = _convertEncoding(_inputText, value, _selectedTargetEncoding);
});
}
},
items: _encodingOptions.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
decoration: const InputDecoration(
labelText: '源编码',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Expanded(
child: DropdownButtonFormField<String>(
value: _selectedTargetEncoding,
onChanged: (value) {
if (value != null) {
setState(() {
_selectedTargetEncoding = value;
_outputText = _convertEncoding(_inputText, _selectedEncoding, value);
});
}
},
items: _encodingOptions.map((option) {
return DropdownMenuItem<String>(
value: option,
child: Text(option),
);
}).toList(),
decoration: const InputDecoration(
labelText: '目标编码',
border: OutlineInputBorder(),
),
),
),
],
),
const SizedBox(height: 16),
Text(
'转换结果',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 8),
TextField(
controller: TextEditingController(text: _outputText),
readOnly: true,
maxLines: 4,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: '转换结果将显示在这里',
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () => _copyToClipboard(_outputText),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: const Text('复制结果'),
),
],
),
],
),
),
// 应用说明
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'应用说明',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 8),
Text(
'1. 在输入文本区域输入要统计和转换的文本',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 4),
Text(
'2. 字符统计区域会自动显示文本的字符数、单词数、行数等信息',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 4),
Text(
'3. 在编码转换区域选择源编码和目标编码,转换结果会自动显示',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 4),
Text(
'4. 点击复制按钮可以将文本复制到剪贴板',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
],
),
),
],
),
),
),
);
}
}
10.2 依赖项
- flutter:Flutter 框架
- flutter/services.dart:提供 Clipboard 类,用于复制文本到剪贴板
- dart:convert:提供编码转换功能
10.3 运行环境
- Flutter SDK:3.0.0 或更高版本
- Dart SDK:2.17.0 或更高版本
- 支持的平台:Android、iOS、Web、Windows、macOS、Linux
10.4 参考资源
更多推荐


所有评论(0)