第一次提交

This commit is contained in:
2025-06-27 20:20:51 +08:00
commit 33f86752de
153 changed files with 6743 additions and 0 deletions

146
lib/providers/api.dart Normal file
View File

@@ -0,0 +1,146 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../config.dart';
import '../models/class_cookie_entry.dart';
const _cookieKey = AppSharedPreferencesKey.cookieKey;
final cookieProvider = StateNotifierProvider<CookieNotifier, Map<String, CookieEntry>>((ref) {
return CookieNotifier();
});
final cookieHeaderProvider = Provider<String>((ref) {
final cookies = ref.watch(cookieProvider);
final now = DateTime.now();
return cookies.entries
.where((e) => e.value.expires == null || e.value.expires!.isAfter(now))
.map((e) => e.value.toCookieString(e.key))
.join('; ');
});
final apiClientProvider = Provider<ApiClient>((ref) {
final cookieHeader = ref.watch(cookieHeaderProvider);
final client = ApiClient(ref: ref);
ref.onDispose(client.dispose);
return client;
});
class ApiClient {
final Ref ref;
final http.Client _client;
static const String baseUrl = AppConfig.apiBaseUrl;
ApiClient({required this.ref, http.Client? client})
: _client = client ?? http.Client(); // 复用或新建
Map<String, String> _buildHeaders({Map<String, String>? extra}) {
final cookieHeader = ref.read(cookieHeaderProvider);
return {
'Content-Type': 'application/json',
if (cookieHeader.isNotEmpty) 'Cookie': cookieHeader,
...?extra,
};
}
Future<http.Response> _send(Future<http.Response> Function(http.Client client) request) async {
final response = await request(_client);
final setCookie = response.headers['set-cookie'];
if (setCookie != null) {
await ref.read(cookieProvider.notifier).updateFromSetCookie(setCookie.split(','));
}
return response;
}
Future<http.Response> get(String path, {Map<String, String>? headers}) {
return _send((client) => client.get(
Uri.parse('$baseUrl/$path'),
headers: _buildHeaders(extra: headers),
));
}
Future<http.Response> post(String path, {Map<String, String>? headers, String? body}) {
return _send((client) => client.post(
Uri.parse('$baseUrl/$path'),
headers: _buildHeaders(extra: headers),
body: body,
));
}
void _checkResponse(http.Response res) {
if (res.statusCode >= 300) {
throw Exception('HTTP ${res.statusCode}: ${res.body}');
}
}
void dispose() {
_client.close(); // 记得释放资源
}
}
class CookieNotifier extends StateNotifier<Map<String, CookieEntry>> {
CookieNotifier() : super({}) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
final list = prefs.getStringList(_cookieKey) ?? [];
final map = <String, CookieEntry>{};
for (final item in list) {
final split = item.split('|');
if (split.length == 2) {
final key = split[0];
final valueMap = Map<String, dynamic>.from(jsonDecode(split[1]));
map[key] = CookieEntry.fromJson(valueMap);
}
}
state = map;
}
Future<void> _persist() async {
final prefs = await SharedPreferences.getInstance();
final list = state.entries.map((e) => '${e.key}|${jsonEncode(e.value.toJson())}').toList();
await prefs.setStringList(_cookieKey, list);
}
Future<void> setAll(Map<String, CookieEntry> map) async {
state = Map<String, CookieEntry>.from(map);
await _persist();
}
Future<void> clear() async {
state = {};
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_cookieKey);
}
Future<void> updateFromSetCookie(List<String> setCookies) async {
final updated = Map<String, CookieEntry>.from(state);
for (final raw in setCookies) {
final parts = raw.split(';');
final kv = parts[0].split('=');
if (kv.length < 2) continue;
final name = kv[0].trim();
final entry = CookieEntry.fromSetCookie(raw);
updated[name] = entry;
}
state = updated;
await _persist();
}
bool get isLoggedIn =>
state.containsKey('auth') || state.containsKey('sessionid');
}

55
lib/providers/theme.dart Normal file
View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/enum_theme.dart';
import '../config.dart';
const _keyThemeMode = AppSharedPreferencesKey.themeMode;
const _keyUseDynamic = AppSharedPreferencesKey.useDynamicColor;
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeModeType>(
(ref) => ThemeModeNotifier(),
);
final useDynamicColorProvider = StateNotifierProvider<UseDynamicColorNotifier, bool>(
(ref) => UseDynamicColorNotifier(),
);
class UseDynamicColorNotifier extends StateNotifier<bool> {
UseDynamicColorNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
state = prefs.getBool(_keyUseDynamic) ?? AppConfig.useDynamicColor;
}
Future<void> set(bool value) async {
state = value;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_keyUseDynamic, value);
}
}
class ThemeModeNotifier extends StateNotifier<ThemeModeType> {
ThemeModeNotifier() : super(ThemeModeType.system) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
final index = prefs.getInt(_keyThemeMode);
if (index != null) {
state = ThemeModeType.fromIndex(index);
} else state = AppConfig.themeMode;
}
Future<void> set(ThemeModeType mode) async {
state = mode;
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_keyThemeMode, mode.index);
}
}