Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
- 添加了备份数据文件导出功能 [#50](https://github.com/OctagonalStar/arabic_learning/issues/50)
- 添加了易混词偏好配置
- 添加了词汇查找功能 [#49](https://github.com/OctagonalStar/arabic_learning/issues/49)
- 添加了常见问题页面

### Improvement

- 优化了选择题单词挑选逻辑 [#48](https://github.com/OctagonalStar/arabic_learning/issues/48)

### Fix

- 将设置选项重新放回首页

## v0.1.12 - 2025-12-27 - (000112)

### Added
Expand Down
53 changes: 53 additions & 0 deletions assets/help/audio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 点击发音按钮后没有声音

## 说明

程序默认下是使用设备自带的无障碍功能进行文本转语音的。
有的时候即使在软件内使用了此类功能也可能由于其他原因无法正常发音。

> 通常来说,国产手机能正常发音的很少。
> 苹果手机(貌似)在浏览器端可以发音。
> Windows即使安装了语音包也不能使用。

## 解决方案

### 1. 检查你的音量

有时候可能只是静音了而已...

### 2. 调整系统文本转语音设置

> 经过测试vivo手机无论如何都不行,请移步下方的"5. 使用神经网络合成语音"。

查找设备设置中"Text To Speech"或"文本转语音"选项,检查是否有阿拉伯语(国际符号为ar-00或ar-SA)支持(由于手机厂商多样性,无法保证所有的手机都支持阿拉伯语)。

在"文本转语音"引擎的设置中或许有添加语言的相关选项。

### 3. 调整系统语言设置

> 在进行此步骤之前请一定要记住你手机系统的语言设置在那个地方。
> 不要用搜索功能。你一旦换到阿语之后,在设置中是无法使用中文查找设置项的。
> (要是你不知道怎么调回中文,就不要尝试此步骤)
> 已确认此步骤对Windows系统无效。

- 在手机设置中添加阿拉伯语语言。
- 将其设置为系统语言。
- 重启软件(最好在设置里使用"强行停止",不仅仅是在多任务里滑一下)
- 再次测试发音。

### 4. 调整发音音源

- 打开Ar学软件内的设置。
- 下滑找到"选择文本转语音接口"。
- 自行调整合适的音源。

> 这种方法将使得应用不再使用你设备的文本转语音。

### 5. 使用神经网络合成语音

- 打开Ar学软件内的设置。
- 下滑找到"下载文本转语音神经网络模型",点击进去。
- 点开始下载,然后等着。

> 由于网络原因,下载可能非常慢。
> 你可以选择耐心等待,或者自行想办法解决。
2 changes: 1 addition & 1 deletion lib/funcs/utili.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Future<List<dynamic>> playTextToSpeech(String text, BuildContext context, {doubl
FlutterTts flutterTts = FlutterTts();
if(!(await flutterTts.getLanguages).toString().contains("ar") && context.mounted) {
context.read<Global>().logger.warning("[TTS]用户设备不支持AR语言TTS");
return [false, "你的设备似乎未安装阿拉伯语语言或不支持阿拉伯语文本转语音功能,语音可能无法正常播放。\n你可以尝试在 设置 - 系统语言 - 添加语言 中添加阿拉伯语。\n实在无法使用可在设置页面启用备用音频源(需要网络)"];
return [false, "你的设备似乎未安装阿拉伯语语言或不支持阿拉伯语文本转语音功能,语音可能无法正常播放。\n你可以在 设置-常见问题 中找到可能的解决方案"];
}
await flutterTts.setLanguage("ar");
await flutterTts.setPitch(1.0);
Expand Down
67 changes: 28 additions & 39 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ class MyHomePage extends StatefulWidget {

class _MyHomePageState extends State<MyHomePage> {
late List<Widget> _pageList;
int currentIndex = 1;
bool onSlip = false;
final PageController _pageController = PageController(initialPage: 1);
final PageController _pageController = PageController(initialPage: 0);
static const Duration _duration = Duration(milliseconds: 500);
bool disPlayedFirst = false;

Expand All @@ -132,28 +130,33 @@ class _MyHomePageState extends State<MyHomePage> {
// 侧边导航栏
NavigationRail(
minWidth: MediaQuery.of(context).size.width * 0.05,
selectedIndex: currentIndex,
selectedIndex: _pageController.hasClients ? _pageController.page!.round() : 0,
onDestinationSelected: (int index) {
_onNavigationTapped(index);
},
labelType: NavigationRailLabelType.selected,
backgroundColor: Theme.of(context).colorScheme.onPrimary.withAlpha(150),
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: Text('学习'),
),
NavigationRailDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: Text('主页'),
),
NavigationRailDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: Text('学习'),
),
NavigationRailDestination(
icon: Icon(Icons.edit_outlined),
selectedIcon: Icon(Icons.edit),
label: Text('测试'),
),
NavigationRailDestination(
icon: Icon(Icons.settings_applications_outlined),
selectedIcon: Icon(Icons.settings_applications),
label: Text('设置'),
),
],
),
// 垂直分隔线
Expand All @@ -164,10 +167,7 @@ class _MyHomePageState extends State<MyHomePage> {
scrollDirection: Axis.vertical,
controller: _pageController,
onPageChanged: (index) {
if (onSlip) return;
setState(() {
currentIndex = index;
});
setState(() {});
},
// physics: const NeverScrollableScrollPhysics(), // 禁用滑动
children: _pageList,
Expand All @@ -188,17 +188,14 @@ class _MyHomePageState extends State<MyHomePage> {
controller: _pageController,
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
if (onSlip) return;
setState(() {
currentIndex = index;
});
setState(() {});
},
children: _pageList,
),
),
// 底部导航栏
NavigationBar(
selectedIndex: currentIndex,
selectedIndex: _pageController.hasClients ? _pageController.page!.round() : 0,
onDestinationSelected: (int index) {
_onNavigationTapped(index);
},
Expand All @@ -207,21 +204,26 @@ class _MyHomePageState extends State<MyHomePage> {
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
backgroundColor: Theme.of(context).colorScheme.onPrimary.withAlpha(150),
destinations: const [
NavigationDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: '学习',
),
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '主页',
),
NavigationDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: '学习',
),
NavigationDestination(
icon: Icon(Icons.edit_outlined),
selectedIcon: Icon(Icons.edit),
label: '测试',
),
NavigationDestination(
icon: Icon(Icons.settings_applications_outlined),
selectedIcon: Icon(Icons.settings_applications),
label: '设置',
),
]
)
],
Expand All @@ -230,18 +232,11 @@ class _MyHomePageState extends State<MyHomePage> {

// 统一的导航点击处理
void _onNavigationTapped(int index) {
onSlip = true;
_pageController.animateToPage(
index,
duration: _duration,
curve: StaticsVar.curve,
);
Future.delayed(_duration, () {
onSlip = false;
});
setState(() {
currentIndex = index;
});
}

final TextEditingController controller = TextEditingController();
Expand Down Expand Up @@ -364,9 +359,10 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
_pageList = [
LearningPage(),
HomePage(),
TestPage()
LearningPage(),
TestPage(),
SettingPage()
];
return Scaffold(
backgroundColor: context.read<Global>().globalConfig.egg.stella ? Colors.transparent : null,
Expand All @@ -382,13 +378,6 @@ class _MyHomePageState extends State<MyHomePage> {
launchUrl(Uri.parse("https://github.com/OctagonalStar/arabic_learning/releases/latest"));
}
),
IconButton(
onPressed: () {
context.read<Global>().uiLogger.info("跳转: MyHomePage => SettingPage");
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SettingPage()));
},
icon: Icon(Icons.settings)
)
],
),
body: LayoutBuilder(
Expand Down
86 changes: 57 additions & 29 deletions lib/pages/setting_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:convert';

import 'package:arabic_learning/sub_pages_builder/setting_pages/debug_page.dart';
import 'package:arabic_learning/vars/statics_var.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
Expand All @@ -9,7 +9,9 @@ import 'package:url_launcher/url_launcher.dart';

import 'package:arabic_learning/funcs/ui.dart';
import 'package:arabic_learning/vars/global.dart';
import 'package:arabic_learning/sub_pages_builder/setting_pages/item_widget.dart';
import 'package:arabic_learning/sub_pages_builder/setting_pages/help_page.dart' show HelpPage;
import 'package:arabic_learning/sub_pages_builder/setting_pages/item_widget.dart'show SettingItem;
import 'package:arabic_learning/sub_pages_builder/setting_pages/debug_page.dart' show DebugPage;
import 'package:arabic_learning/sub_pages_builder/setting_pages/about_page.dart' show AboutPage;
import 'package:arabic_learning/sub_pages_builder/setting_pages/data_download_page.dart' show DownloadPage;
import 'package:arabic_learning/sub_pages_builder/setting_pages/model_download_page.dart' show ModelDownload;
Expand All @@ -27,36 +29,62 @@ class _SettingPage extends State<SettingPage> {
@override
Widget build(BuildContext context) {
context.read<Global>().uiLogger.fine("构建 SettingPage");
return Scaffold(
appBar: AppBar(title: Text("设置")),
body: Consumer<Global>(
builder: (context, value, child) {
return ListView(
children: [
SettingItem(
title: "常规设置",
padding: EdgeInsets.all(8.0),
children: regularSetting(context),
),
SettingItem(
title: "学习设置",
children: dataSetting(context),
),
SettingItem(
title: "音频设置",
children: audioSetting(context),
),
SettingItem(
title: "关于",
children: aboutSetting(context),
),
],
);
},
),
return Consumer<Global>(
builder: (context, value, child) {
return ListView(
children: [
SettingItem(
title: "帮助",
children: helpEssay(context)
),
SettingItem(
title: "常规设置",
padding: EdgeInsets.all(8.0),
children: regularSetting(context),
),
SettingItem(
title: "学习设置",
children: dataSetting(context),
),
SettingItem(
title: "音频设置",
children: audioSetting(context),
),
SettingItem(
title: "关于",
children: aboutSetting(context),
),
],
);
},
);
}

List<Widget> helpEssay(BuildContext context) {
MediaQueryData mediaQuery = MediaQuery.of(context);
return [
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size.fromHeight(mediaQuery.size.height * 0.08),
backgroundColor: Theme.of(context).colorScheme.onPrimary.withAlpha(150),
shape: RoundedRectangleBorder(borderRadius: StaticsVar.br)
),
onPressed: (){
context.read<Global>().uiLogger.info("跳转: SettingPage => HelpPage");
Navigator.push(context, MaterialPageRoute(builder: (context) => HelpPage()));
},
child: Row(
children: [
Icon(Icons.help, size: 24.0),
SizedBox(width: mediaQuery.size.width * 0.01),
Expanded(child: Text("常见问题", textAlign: TextAlign.start)),
Icon(Icons.arrow_forward_ios)
]
),
)
];
}

List<Widget> regularSetting(BuildContext context) {
MediaQueryData mediaQuery = MediaQuery.of(context);
return [
Expand Down
35 changes: 35 additions & 0 deletions lib/sub_pages_builder/setting_pages/help_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';

class HelpPage extends StatelessWidget{
const HelpPage({super.key});

@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getHelpMarkDown(),
builder: (context, helpEssay) {
if(!helpEssay.hasData) return CircularProgressIndicator();

return Scaffold(
appBar: AppBar(title: Text("常见问题")),
body: ListView(
children: [
ExpansionTile(
title: Text("点击发音按钮后没有声音"),
children: [
MarkdownBody(data: helpEssay.data?.elementAt(0) ?? "")
],
)
],
),
);
}
);
}
}

Future<List<String>> getHelpMarkDown() async {
return [await rootBundle.loadString('assets/help/audio.md')];
}
Loading