第一次提交

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

37
lib/routes/about.dart Normal file
View File

@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:gap/gap.dart';
import '../../src/version.dart';
import '../../config.dart';
class AboutPage extends HookWidget {
const AboutPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('关于')),
body: ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('images/favicon.png', width: 120, height: 120).clipOval(),
const SizedBox(height: 20),
Text(
'拾糖论坛',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 24),
),
],
).constrained(height: 200, width: double.infinity),
const Divider(height: 16),
ListTile(
title: const Text('App 版本'),
subtitle: Text('v${packageVersion}'),
),
],
)
);
}
}

120
lib/routes/root.dart Normal file
View File

@@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:styled_widget/styled_widget.dart';
import '../config.dart';
import '../demo.dart';
import './root_pages/home.dart';
import './root_pages/me.dart';
import '../models/class_destination_item.dart';
const destinations = [
DestinationItem(
label: '首页',
icon: Icons.home,
child: HomePage(),
title: '拾糖',
),
// DestinationItem(
// label: '展示',
// icon: Icons.message,
// child: AllWidgetsDemo(),
// title: '组件演示',
// ),
DestinationItem(
label: '我的',
icon: Icons.person,
child: MePage(),
),
];
class RootRoute extends HookConsumerWidget {
const RootRoute({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final index = useState(0);
final controller = usePageController();
final jumping = useState(false);
useEffect(() {
if (!jumping.value) return null;
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (controller.hasClients) {
await controller.animateToPage(
index.value,
duration: AppAnimationConfig.duration,
curve: AppAnimationConfig.pageViewCurve,
);
jumping.value = false;
}
});
return null;
}, [index.value, jumping.value]);
return LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= AppConfig.wideWidth;
return Scaffold(
appBar: AppBar(
title: Text(destinations[index.value].title ?? destinations[index.value].label),
),
body: Row(
children: [
if (isWide)
NavigationRail(
backgroundColor: Theme.of(context).colorScheme.surface,
selectedIndex: index.value,
onDestinationSelected: (i) {
if (i != index.value) {
index.value = i;
jumping.value = true;
}
},
labelType: NavigationRailLabelType.all,
destinations: destinations.map((d) {
return NavigationRailDestination(
icon: Icon(d.icon),
label: Text(d.label),
);
}).toList(),
),
// 内容区域
Expanded(
child: PageView(
controller: controller,
onPageChanged: (i) {
if (!jumping.value) {
index.value = i;
}
},
children: <Widget>[
for (final d in destinations) d.child,
],
),
),
],
),
bottomNavigationBar: isWide
? null
: NavigationBar(
selectedIndex: index.value,
onDestinationSelected: (i) {
if (i != index.value) {
index.value = i;
jumping.value = true;
}
},
destinations: destinations.map((d) {
return NavigationDestination(
icon: Icon(d.icon),
label: d.label,
);
}).toList(),
),
);
},
);
}
}

View File

@@ -0,0 +1,40 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import '../../providers/api.dart';
import '../../api/capatcha.dart';
class HomePage extends HookConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final cookies = ref.watch(cookieHeaderProvider);
final future = useMemoized(() async {
final client = ref.read(apiClientProvider);
final res = await client.get('');
return res.bodyBytes;
}, []);
final snapshot = useFuture(future);
return ListView(
padding: EdgeInsets.all(16),
children:[
// if (snapshot.hasData) Text('${utf8.decode(snapshot.data!)}'),
const Text('HTTP API Client 状态:'),
const Gap(16),
Text('Cookies: ${cookies}'),
if (snapshot.hasData) ...[
const Gap(16),
Text('${utf8.decode(snapshot.data!)}')
]
]
);
}
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
class MePage extends HookConsumerWidget {
const MePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('images/test.png', width: 120, height: 120).clipOval(),
const SizedBox(height: 20),
Text(
'UnknownMp',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 24),
),
],
).constrained(height: 320, width: double.infinity),
const Divider(height: 16),
Column(
children: [
InkWell(
onTap: () => context.push('/settings'),
child: ListTile(
leading: const Icon(Icons.settings),
title: const Text('设置'),
trailing: const Icon(Icons.arrow_forward),
),
),
]
),
],
);
}
}

40
lib/routes/settings.dart Normal file
View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart';
import '../config.dart';
class SettingsPage extends HookConsumerWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: ListView(
children: [
const Gap(8),
InkWell(
onTap: () => context.push('/settings/theme'),
child: ListTile(
leading: const Icon(Icons.palette),
title: const Text('外观'),
// subtitle: const Text('亮 / 暗模式, 动态取色?'),
),
),
const Divider(height: 16),
InkWell(
onTap: () => context.push('/about'),
child: ListTile(
leading: const Icon(Icons.info),
title: const Text('关于'),
subtitle: Text('拾糖论坛'),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:gap/gap.dart';
import '../../config.dart';
import '../../providers/theme.dart';
import '../../models/enum_theme.dart';
class SettingsThemePage extends HookConsumerWidget {
const SettingsThemePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('外观')),
body: ListView(
children: [
const Gap(16),
ListTile(
title: const Text('配色方案'),
trailing: DropdownMenu<ThemeModeType>(
initialSelection: ref.watch(themeModeProvider),
onSelected: (value) { if (value != null) ref.read(themeModeProvider.notifier).set(value); },
dropdownMenuEntries: const [
DropdownMenuEntry(value: ThemeModeType.system, label: '跟随系统'),
DropdownMenuEntry(value: ThemeModeType.light, label: '亮色'),
DropdownMenuEntry(value: ThemeModeType.dark, label: '暗色'),
],
),
),
const Gap(16),
SwitchListTile(
title: Text('动态配色'),
value: ref.watch(useDynamicColorProvider),
onChanged: (value) { ref.read(useDynamicColorProvider.notifier).set(value); },
),
//const Divider(height: 16),
],
),
);
}
}