example/lib/pages/settings/settings_page.dart (419 lines of code) (raw):
// Copyright © 2025 Alibaba Cloud. All rights reserved.
//
// Author: keria
// Date: 2025/2/20
// Brief: 设置页面
import 'dart:io';
import 'package:aliplayer_widget/aliplayer_widget_lib.dart';
import 'package:aliplayer_widget_example/constants/demo_constants.dart';
import 'package:aliplayer_widget_example/constants/page_routes.dart';
import 'package:aliplayer_widget_example/manager/sp_manager.dart';
import 'package:aliplayer_widget_example/utils/http_util.dart';
import 'package:aliplayer_widget_example/utils/navigate_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// 设置页面
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
// 数据结构定义
final List<SettingItem> items = [
SectionHeader(title: "通用"),
ButtonItem(
title: "链接设置",
onPressed: () {
NavigateUtil.pushWithRoute(context, PageRoutes.link);
},
),
ButtonItem(
title: "清除缓存",
onPressed: () {
AliPlayerWidgetController.clearCaches();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("清除缓存成功"),
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
),
);
},
),
SectionHeader(title: "配置"),
SelectionItem<ShortVideoResource>(
title: "短视频资源",
optionsLoader: _fetchVideoResources,
selectedOptionLoader: (options) async {
return _fetchSelectedVideoResource(options);
},
onSelected: (resource) {
SPManager.instance.saveString(
DemoConstants.keyDramaInfoListUrl,
resource.url,
);
},
displayFormatter: (resource) => resource.title,
),
SectionHeader(title: "调试"),
ButtonItem(
title: "调试页面",
onPressed: () {
NavigateUtil.pushWithRoute(context, PageRoutes.debug);
},
),
SectionHeader(title: "其它"),
TextItem(
title: "设备 UUID",
subtitleLoader: () async {
final version = await FlutterAliplayer.getDeviceUUID();
return version;
},
),
TextItem(
title: "SDK 版本号",
subtitleLoader: () async {
final sdkVersion = await FlutterAliplayer.getSDKVersion();
return sdkVersion;
},
),
TextItem(
title: "Widget 版本号",
subtitleLoader: () async {
final version = AliPlayerWidgetController.getWidgetVersion();
return version;
},
),
TextItem(
title: "Dart 版本号",
subtitleLoader: () async {
return Platform.version;
},
),
];
return Scaffold(
appBar: AppBar(
title: const Text("Settings"),
backgroundColor: Colors.orangeAccent,
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
if (item is SectionHeader) {
return _buildSectionHeader(item);
}
final Widget itemView;
if (item is SwitchItem) {
itemView = _buildSwitchItem(item);
} else if (item is SelectionItem) {
itemView = _buildSelectionItem(item);
} else if (item is ButtonItem) {
itemView = _buildButtonItem(item);
} else if (item is TextItem) {
itemView = _buildTextItem(item);
} else {
return const SizedBox.shrink();
}
return Container(
color: Colors.white,
child: itemView,
);
},
),
);
}
// 构建 Section Header
Widget _buildSectionHeader(SectionHeader item) {
return Padding(
padding: const EdgeInsets.all(8),
child: Text(
item.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
);
}
// 构建 Switch Item
Widget _buildSwitchItem(SwitchItem item) {
return FutureBuilder<bool>(
future: item.valueLoader(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return ListTile(
title: Text(item.title),
trailing: const CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return ListTile(
title: Text(item.title),
subtitle: Text("Error: ${snapshot.error}"),
);
} else if (snapshot.hasData) {
return ListTile(
title: Text(item.title),
trailing: Switch(
value: snapshot.data!,
onChanged: (value) {
setState(() {
item.onChanged(value);
});
},
),
);
}
return const SizedBox.shrink();
},
);
}
// 这个方法将用于处理所有 SelectionItem 类型
Widget _buildSelectionItem(SettingItem item) {
if (item is SelectionItem<String>) {
return _buildTypedSelectionItem<String>(item);
} else if (item is SelectionItem<ShortVideoResource>) {
return _buildTypedSelectionItem<ShortVideoResource>(item);
}
return const ListTile(
title: Text("未支持的 SelectionItem 类型"),
subtitle: Text("请在 _buildSelectionItem 中添加对应类型支持"),
);
}
// 构建 Selection Item
// 构建 Selection Item
Widget _buildTypedSelectionItem<T>(SelectionItem<T> item) {
return FutureBuilder<List<T>>(
future: item.optionsLoader(),
builder: (context, optionsSnapshot) {
if (optionsSnapshot.connectionState == ConnectionState.waiting) {
return ListTile(
title: Text(item.title),
trailing: const CircularProgressIndicator(),
);
} else if (optionsSnapshot.hasError) {
return ListTile(
title: Text(item.title),
subtitle: Text("Error: ${optionsSnapshot.error}"),
);
} else if (optionsSnapshot.hasData) {
final options = optionsSnapshot.data!;
return FutureBuilder<T?>(
future: item.selectedOptionLoader != null
? item.selectedOptionLoader!(options)
: Future.value(options.isNotEmpty ? options.first : null),
builder: (context, selectedSnapshot) {
// 显示加载中状态
if (selectedSnapshot.connectionState == ConnectionState.waiting) {
return ListTile(
title: Text(item.title),
trailing: const CircularProgressIndicator(strokeWidth: 2),
);
}
// 获取当前选中项
final selectedOption = selectedSnapshot.data;
return ListTile(
title: Text(item.title),
subtitle: selectedOption != null
? Text(item.displayFormatter(selectedOption))
: const Text("未选择"),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () async {
final selected = await showDialog<T>(
context: context,
builder: (context) => SimpleDialog(
title: Text(item.title),
children: [
...options.map<Widget>(
(T option) {
final isSelected = selectedOption != null &&
item.displayFormatter(selectedOption) ==
item.displayFormatter(option);
return ListTile(
title: Text(item.displayFormatter(option)),
// 在当前选中项旁边显示勾选图标
trailing: isSelected
? const Icon(Icons.check, color: Colors.blue)
: null,
onTap: () {
Navigator.pop(context, option);
},
);
},
),
// 添加顶部的清除选项
if (selectedOption != null)
ListTile(
leading: const Icon(
Icons.delete_outline,
color: Colors.red,
),
title: const Text(
"清除选择",
style: TextStyle(color: Colors.red),
),
onTap: () {
Navigator.pop(context);
_showClearConfirmDialog<T>(context, item);
},
),
],
),
);
if (selected != null) {
setState(() {
item.onSelected(selected);
});
}
},
);
},
);
}
return const SizedBox.shrink();
},
);
}
// 显示清除确认对话框
Future<void> _showClearConfirmDialog<T>(
BuildContext context,
SelectionItem<T> item,
) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text("确认清除"),
content: Text("确定要清除${item.title}的设置吗?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text("取消"),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text("确定", style: TextStyle(color: Colors.red)),
),
],
),
);
if (confirmed == true) {
setState(() {
if (item is SelectionItem<ShortVideoResource>) {
SPManager.instance.remove(DemoConstants.keyDramaInfoListUrl);
}
});
}
}
// 构建 Button Item
Widget _buildButtonItem(ButtonItem item) {
return ListTile(
title: Text(item.title),
onTap: item.onPressed,
);
}
// 构建 Text Item
Widget _buildTextItem(TextItem item) {
return FutureBuilder<String>(
future: item.subtitleLoader(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return ListTile(
title: Text(item.title),
subtitle: const Text("Loading..."),
);
} else if (snapshot.hasError) {
return ListTile(
title: Text(item.title),
subtitle: Text("Error: ${snapshot.error}"),
);
} else if (snapshot.hasData) {
return ListTile(
title: Text(item.title),
subtitle: Text(snapshot.data!),
onTap: () {
Clipboard.setData(ClipboardData(text: snapshot.data ?? ""));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Copied to clipboard"),
duration: Duration(seconds: 2),
),
);
},
);
}
return const SizedBox.shrink();
},
);
}
// 获取短视频资源列表
static Future<List<ShortVideoResource>> _fetchVideoResources() async {
try {
// 发起 HTTP 请求获取视频数据
final response = await HTTPUtil.instance.get(
DemoConstants.defaultDramaInfoListTotalUrl,
);
// 如果请求失败,返回空列表
if (response == null || response is! List) {
debugPrint("[error]: Failed to load video resources");
return [];
}
return response.map((item) => ShortVideoResource.fromJson(item)).toList();
} catch (e) {
debugPrint("Error loading drama info list: $e");
return [];
}
}
// 获取当前选中的短视频资源
static ShortVideoResource? _fetchSelectedVideoResource(
List<ShortVideoResource> options,
) {
if (options.isEmpty) {
return null;
}
// 从 SharedPreferences 中获取保存的短视频链接
final savedLink = SPManager.instance.getString(
DemoConstants.keyDramaInfoListUrl,
);
if (savedLink == null) {
return null;
}
// 找到匹配的短视频资源
final matches = options.where((option) => option.url == savedLink);
return matches.isNotEmpty ? matches.first : null;
}
}
// 抽象数据结构
abstract class SettingItem {}
class SectionHeader extends SettingItem {
final String title;
SectionHeader({required this.title});
}
class SwitchItem extends SettingItem {
final String title;
final Future<bool> Function() valueLoader;
final ValueChanged<bool> onChanged;
SwitchItem({
required this.title,
required this.valueLoader,
required this.onChanged,
});
}
class SelectionItem<T> extends SettingItem {
final String title;
final Future<List<T>> Function() optionsLoader;
final Future<T?> Function(List<T>)? selectedOptionLoader;
final ValueChanged<T> onSelected;
final String Function(T) displayFormatter;
SelectionItem({
required this.title,
required this.optionsLoader,
this.selectedOptionLoader,
required this.onSelected,
required this.displayFormatter,
});
}
class ButtonItem extends SettingItem {
final String title;
final VoidCallback onPressed;
ButtonItem({
required this.title,
required this.onPressed,
});
}
class TextItem extends SettingItem {
final String title;
final Future<String> Function() subtitleLoader;
TextItem({
required this.title,
required this.subtitleLoader,
});
}
/// 定义短视频资源的数据模型
class ShortVideoResource {
final String title;
final String url;
ShortVideoResource({required this.title, required this.url});
factory ShortVideoResource.fromJson(Map<String, dynamic> json) {
return ShortVideoResource(
title: json['title'],
url: json['url'],
);
}
}