add theme mode feature
This commit is contained in:
parent
143756cff6
commit
7205018e20
22 changed files with 452 additions and 89 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.configuration.updateBuildConfiguration": "automatic"
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
|||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
ndkVersion "25.1.8937393"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
|
|
|||
BIN
assets/switch.riv
Normal file
BIN
assets/switch.riv
Normal file
Binary file not shown.
|
|
@ -4,4 +4,5 @@ class HiveConstant {
|
|||
static String get hiveBoxMangaHistory => "_manga_box_history_";
|
||||
static String get hiveBoxMangaSource => "_manga_box_source_";
|
||||
static String get hiveBoxMangaFilter => "_manga_box_filter_";
|
||||
static String get hiveBoxAppSettings => "_app_box_settings_";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:mangayomi/constant.dart';
|
||||
import 'package:mangayomi/models/manga_history.dart';
|
||||
|
|
@ -8,6 +9,9 @@ import 'package:mangayomi/models/model_manga.dart';
|
|||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/source/source_model.dart';
|
||||
|
||||
import 'views/more/settings/appearance/flex_scheme_color_provider.dart';
|
||||
import 'views/more/settings/appearance/thememode_provider.dart';
|
||||
|
||||
void main() async {
|
||||
await Hive.initFlutter();
|
||||
Hive.registerAdapter(ModelMangaAdapter());
|
||||
|
|
@ -19,6 +23,7 @@ void main() async {
|
|||
await Hive.openBox<SourceModel>(HiveConstant.hiveBoxMangaSource);
|
||||
await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
|
||||
await Hive.openBox(HiveConstant.hiveBoxMangaFilter);
|
||||
await Hive.openBox(HiveConstant.hiveBoxAppSettings);
|
||||
runApp(const ProviderScope(child: MyApp()));
|
||||
}
|
||||
|
||||
|
|
@ -28,25 +33,45 @@ class MyApp extends ConsumerWidget {
|
|||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ThemeData themeLight = FlexThemeData.light(
|
||||
colors: ref.watch(flexSchemeColorProvider),
|
||||
surfaceMode: FlexSurfaceMode.highScaffoldLevelSurface,
|
||||
blendLevel: 24,
|
||||
appBarOpacity: 0.00,
|
||||
subThemesData: const FlexSubThemesData(
|
||||
blendOnLevel: 24,
|
||||
thinBorderWidth: 2.0,
|
||||
unselectedToggleIsColored: true,
|
||||
inputDecoratorRadius: 24.0,
|
||||
chipRadius: 24.0,
|
||||
dialogBackgroundSchemeColor: SchemeColor.background,
|
||||
),
|
||||
useMaterial3ErrorColors: true,
|
||||
visualDensity: FlexColorScheme.comfortablePlatformDensity,
|
||||
useMaterial3: true,
|
||||
fontFamily: GoogleFonts.aBeeZee().fontFamily,
|
||||
);
|
||||
ThemeData themeDark = FlexThemeData.dark(
|
||||
colors: ref.watch(flexSchemeColorProvider),
|
||||
surfaceMode: FlexSurfaceMode.highScaffoldLevelSurface,
|
||||
blendLevel: 24,
|
||||
appBarOpacity: 0.00,
|
||||
subThemesData: const FlexSubThemesData(
|
||||
blendOnLevel: 24,
|
||||
thinBorderWidth: 2.0,
|
||||
unselectedToggleIsColored: true,
|
||||
inputDecoratorRadius: 24.0,
|
||||
chipRadius: 24.0,
|
||||
dialogBackgroundSchemeColor: SchemeColor.background,
|
||||
),
|
||||
useMaterial3ErrorColors: true,
|
||||
visualDensity: FlexColorScheme.comfortablePlatformDensity,
|
||||
useMaterial3: true,
|
||||
fontFamily: GoogleFonts.aBeeZee().fontFamily,
|
||||
);
|
||||
final router = ref.watch(routerProvider);
|
||||
return MaterialApp.router(
|
||||
theme: FlexThemeData.light(
|
||||
colors: ThemeAA.schemes[6].light,
|
||||
surfaceMode: FlexSurfaceMode.highScaffoldLevelSurface,
|
||||
blendLevel: 24,
|
||||
appBarOpacity: 0.00,
|
||||
subThemesData: const FlexSubThemesData(
|
||||
blendOnLevel: 24,
|
||||
thinBorderWidth: 2.0,
|
||||
unselectedToggleIsColored: true,
|
||||
inputDecoratorRadius: 24.0,
|
||||
chipRadius: 24.0,
|
||||
dialogBackgroundSchemeColor: SchemeColor.background,
|
||||
),
|
||||
useMaterial3ErrorColors: true,
|
||||
visualDensity: FlexColorScheme.comfortablePlatformDensity,
|
||||
useMaterial3: true,
|
||||
),
|
||||
theme: ref.watch(themeModeProvider) ? themeLight : themeDark,
|
||||
debugShowCheckedModeBanner: false,
|
||||
routeInformationParser: router.routeInformationParser,
|
||||
routerDelegate: router.routerDelegate,
|
||||
|
|
|
|||
|
|
@ -23,3 +23,6 @@ final hiveBoxMangaFilterProvider = Provider<Box>((ref) {
|
|||
final hiveBoxMangaSourceProvider = Provider<Box<SourceModel>>((ref) {
|
||||
return Hive.box<SourceModel>(HiveConstant.hiveBoxMangaSource);
|
||||
});
|
||||
final hiveBoxSettings = Provider<Box>((ref) {
|
||||
return Hive.box(HiveConstant.hiveBoxAppSettings);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import 'package:mangayomi/views/manga/detail/manga_reader_detail.dart';
|
|||
import 'package:mangayomi/views/manga/home/home.dart';
|
||||
import 'package:mangayomi/views/manga/reader/manga_reader_view.dart';
|
||||
import 'package:mangayomi/views/more/more_screen.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/appearance_screen.dart';
|
||||
import 'package:mangayomi/views/more/settings/settings_screen.dart';
|
||||
import 'package:mangayomi/views/updates/updates_screen.dart';
|
||||
|
||||
final routerProvider = Provider<GoRouter>((ref) {
|
||||
|
|
@ -149,6 +151,32 @@ class AsyncRouterNotifier extends ChangeNotifier {
|
|||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/settings",
|
||||
name: "settings",
|
||||
builder: (context, state) {
|
||||
return const SettingsScreen();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: const SettingsScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/appearance",
|
||||
name: "appearance",
|
||||
builder: (context, state) {
|
||||
return const AppearanceScreen();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return CustomTransition(
|
||||
key: state.pageKey,
|
||||
child: const AppearanceScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ class _HistoryScreenState extends ConsumerState<HistoryScreen> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 60,
|
||||
height: 90,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final model = ModelManga(
|
||||
|
|
@ -127,7 +129,7 @@ class _HistoryScreenState extends ConsumerState<HistoryScreen> {
|
|||
child: cachedNetworkImage(
|
||||
imageUrl: element.modelManga.imageUrl!,
|
||||
width: 60,
|
||||
height: 100,
|
||||
height: 90,
|
||||
fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
|
|
@ -168,7 +170,7 @@ class _HistoryScreenState extends ConsumerState<HistoryScreen> {
|
|||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment:
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
for (var i = 0;
|
||||
i < widget.modelManga!.genre!.length;
|
||||
|
|
@ -402,7 +403,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView>
|
|||
child: Column(
|
||||
children: const [
|
||||
Icon(
|
||||
Icons.travel_explore,
|
||||
FontAwesomeIcons.earthAfrica,
|
||||
size: 25,
|
||||
),
|
||||
SizedBox(
|
||||
|
|
|
|||
|
|
@ -21,25 +21,28 @@ class ReadMoreWidgetState extends State<ReadMoreWidget>
|
|||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
ExpandableText(
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
onExpandedChanged: (ok) {
|
||||
setState(() => expanded = ok);
|
||||
widget.onChanged(ok);
|
||||
},
|
||||
expandOnTextTap: true,
|
||||
widget.text,
|
||||
expandText: '',
|
||||
maxLines: 3,
|
||||
expanded: false,
|
||||
onPrefixTap: () {
|
||||
setState(() => expanded = !expanded);
|
||||
widget.onChanged(expanded);
|
||||
},
|
||||
linkColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
animation: true,
|
||||
collapseOnTextTap: true,
|
||||
prefixText: '',
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: ExpandableText(
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
onExpandedChanged: (ok) {
|
||||
setState(() => expanded = ok);
|
||||
widget.onChanged(ok);
|
||||
},
|
||||
expandOnTextTap: true,
|
||||
widget.text.trim(),
|
||||
expandText: '',
|
||||
maxLines: 3,
|
||||
expanded: false,
|
||||
onPrefixTap: () {
|
||||
setState(() => expanded = !expanded);
|
||||
widget.onChanged(expanded);
|
||||
},
|
||||
linkColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
animation: true,
|
||||
collapseOnTextTap: true,
|
||||
prefixText: '',
|
||||
),
|
||||
),
|
||||
if (!expanded)
|
||||
Positioned(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class MoreScreen extends StatelessWidget {
|
||||
const MoreScreen({super.key});
|
||||
|
|
@ -17,17 +18,17 @@ class MoreScreen extends StatelessWidget {
|
|||
const Divider(
|
||||
color: Colors.grey,
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading:
|
||||
const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
|
||||
subtitle: const Text('Filter all entries in your library'),
|
||||
title: const Text('Donloaded only'),
|
||||
trailing: Switch(
|
||||
value: false,
|
||||
onChanged: (value) {},
|
||||
),
|
||||
),
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// leading:
|
||||
// const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
|
||||
// subtitle: const Text('Filter all entries in your library'),
|
||||
// title: const Text('Donloaded only'),
|
||||
// trailing: Switch(
|
||||
// value: false,
|
||||
// onChanged: (value) {},
|
||||
// ),
|
||||
// ),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading: const SizedBox(
|
||||
|
|
@ -42,45 +43,47 @@ class MoreScreen extends StatelessWidget {
|
|||
const Divider(
|
||||
color: Colors.grey,
|
||||
),
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// leading: const SizedBox(
|
||||
// height: 40, child: Icon(Icons.download_outlined)),
|
||||
// title: const Text('Donwload queue'),
|
||||
// ),
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// leading: Container(
|
||||
// height: 20,
|
||||
// width: 20,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// title: const Text('Categories'),
|
||||
// ),
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// leading: Container(
|
||||
// height: 20,
|
||||
// width: 20,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// title: const Text('Statistics'),
|
||||
// ),
|
||||
// ListTile(
|
||||
// onTap: () {},
|
||||
// leading: const SizedBox(
|
||||
// height: 40,
|
||||
// child: Icon(Icons.settings_backup_restore_sharp)),
|
||||
// title: const Text('Backup and restore'),
|
||||
// ),
|
||||
// const Divider(
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading: const SizedBox(
|
||||
height: 40, child: Icon(Icons.download_outlined)),
|
||||
title: const Text('Donwload queue'),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading: Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
title: const Text('Categories'),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading: Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
title: const Text('Statistics'),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
leading: const SizedBox(
|
||||
height: 40,
|
||||
child: Icon(Icons.settings_backup_restore_sharp)),
|
||||
title: const Text('Backup and restore'),
|
||||
),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
context.push('/settings');
|
||||
},
|
||||
leading: const SizedBox(
|
||||
height: 40, child: Icon(Icons.settings_outlined)),
|
||||
title: const Text('Backup and restore'),
|
||||
title: const Text('Settings'),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {},
|
||||
|
|
|
|||
21
lib/views/more/settings/appearance/appearance_screen.dart
Normal file
21
lib/views/more/settings/appearance/appearance_screen.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/dark_mode_button.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/theme_selector.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/thememode_provider.dart';
|
||||
|
||||
class AppearanceScreen extends ConsumerWidget {
|
||||
const AppearanceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Appearance"),
|
||||
),
|
||||
body: Column(
|
||||
children: const [DarkModeButton(), ThemeSelector()],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
52
lib/views/more/settings/appearance/dark_mode_button.dart
Normal file
52
lib/views/more/settings/appearance/dark_mode_button.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/thememode_provider.dart';
|
||||
import 'package:rive/rive.dart';
|
||||
|
||||
class DarkModeButton extends ConsumerStatefulWidget {
|
||||
const DarkModeButton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<DarkModeButton> createState() => _DarkModeButtonState();
|
||||
}
|
||||
|
||||
class _DarkModeButtonState extends ConsumerState<DarkModeButton> {
|
||||
SMIBool? _bump;
|
||||
|
||||
void _onRiveInit(Artboard artboard) {
|
||||
final controller =
|
||||
StateMachineController.fromArtboard(artboard, 'State Machine 1');
|
||||
artboard.addController(controller!);
|
||||
_bump = controller.findInput<bool>('isDark') as SMIBool;
|
||||
_bump?.value = !ref.watch(themeModeProvider);
|
||||
}
|
||||
|
||||
void _hitBump(bool value) => _bump?.value = value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isLight = ref.watch(themeModeProvider);
|
||||
_hitBump(!isLight);
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
if (!isLight == true) {
|
||||
ref.read(themeModeProvider.notifier).setLightTheme();
|
||||
} else {
|
||||
ref.read(themeModeProvider.notifier).setDarkTheme();
|
||||
}
|
||||
},
|
||||
title: const Text("Theme mode"),
|
||||
subtitle: Text(ref.watch(themeModeProvider) ? 'Light' : 'Dark'),
|
||||
trailing: SizedBox(
|
||||
height: 80,
|
||||
width: 80,
|
||||
child: RiveAnimation.asset(
|
||||
'assets/switch.riv',
|
||||
onInit: _onRiveInit,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/providers/hive_provider.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/thememode_provider.dart';
|
||||
|
||||
final flexSchemeColorProvider =
|
||||
StateNotifierProvider<ThemeColorState, FlexSchemeColor>((ref) {
|
||||
return ThemeColorState(ref);
|
||||
});
|
||||
|
||||
class ThemeColorState extends StateNotifier<FlexSchemeColor> {
|
||||
final Ref _ref;
|
||||
ThemeColorState(this._ref) : super(FlexColor.deepBlue.light) {
|
||||
if (_ref.watch(themeModeProvider)) {
|
||||
if (_ref.watch(hiveBoxSettings).get('FlexColorIndex') != null) {
|
||||
state = ThemeAA
|
||||
.schemes[_ref.watch(hiveBoxSettings).get('FlexColorIndex')].light;
|
||||
}
|
||||
} else {
|
||||
if (_ref.watch(hiveBoxSettings).get('FlexColorIndex') != null) {
|
||||
state = ThemeAA
|
||||
.schemes[_ref.watch(hiveBoxSettings).get('FlexColorIndex')].dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
void setTheme(FlexSchemeColor color, int index) {
|
||||
state = color;
|
||||
_ref.watch(hiveBoxSettings).put('FlexColorIndex', index);
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeAA {
|
||||
static const List<FlexSchemeData> schemes = <FlexSchemeData>[
|
||||
...FlexColor.schemesList,
|
||||
];
|
||||
}
|
||||
103
lib/views/more/settings/appearance/theme_selector.dart
Normal file
103
lib/views/more/settings/appearance/theme_selector.dart
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:mangayomi/providers/hive_provider.dart';
|
||||
import 'package:mangayomi/views/more/settings/appearance/flex_scheme_color_provider.dart';
|
||||
|
||||
class ThemeSelector extends ConsumerStatefulWidget {
|
||||
const ThemeSelector({
|
||||
super.key,
|
||||
this.contentPadding,
|
||||
});
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
|
||||
@override
|
||||
ConsumerState<ThemeSelector> createState() => _ThemeSelectorState();
|
||||
}
|
||||
|
||||
class _ThemeSelectorState extends ConsumerState<ThemeSelector> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int selected =
|
||||
ref.watch(hiveBoxSettings).get('FlexColorIndex', defaultValue: 7);
|
||||
const double height = 45;
|
||||
const double width = height * 1.5;
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final bool isLight = Theme.of(context).brightness == Brightness.light;
|
||||
final ColorScheme scheme = Theme.of(context).colorScheme;
|
||||
return SizedBox(
|
||||
height: 130,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsetsDirectional.only(start: 8, end: 16),
|
||||
physics: const ClampingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: ThemeAA.schemes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
FlexThemeModeOptionButton(
|
||||
flexSchemeColor: isLight
|
||||
? ThemeAA.schemes[index].light
|
||||
: ThemeAA.schemes[index].dark,
|
||||
selected: selected == index,
|
||||
selectedBorder: BorderSide(
|
||||
color: theme.primaryColorLight,
|
||||
width: 4,
|
||||
),
|
||||
unselectedBorder: BorderSide.none,
|
||||
backgroundColor: scheme.background,
|
||||
width: width,
|
||||
height: height,
|
||||
padding: EdgeInsets.zero,
|
||||
borderRadius: 0,
|
||||
onSelect: () {
|
||||
setState(() {
|
||||
selected = index;
|
||||
});
|
||||
isLight
|
||||
? ref
|
||||
.read(flexSchemeColorProvider.notifier)
|
||||
.setTheme(ThemeAA.schemes[selected].light,
|
||||
selected)
|
||||
: ref
|
||||
.read(flexSchemeColorProvider.notifier)
|
||||
.setTheme(ThemeAA.schemes[selected].dark,
|
||||
selected);
|
||||
},
|
||||
optionButtonPadding: EdgeInsets.zero,
|
||||
optionButtonMargin: EdgeInsets.zero,
|
||||
),
|
||||
Text(ThemeAA.schemes[index].name)
|
||||
],
|
||||
),
|
||||
if (selected == index)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: CircleAvatar(
|
||||
radius: 14,
|
||||
backgroundColor: theme.primaryColorLight,
|
||||
child: const Icon(
|
||||
FontAwesomeIcons.check,
|
||||
color: Colors.black,
|
||||
size: 16,
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
26
lib/views/more/settings/appearance/thememode_provider.dart
Normal file
26
lib/views/more/settings/appearance/thememode_provider.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/providers/hive_provider.dart';
|
||||
|
||||
final themeModeProvider = StateNotifierProvider<ThemeModeState, bool>((ref) {
|
||||
return ThemeModeState(ref);
|
||||
});
|
||||
final onPressedProvider = StateProvider<String>((ref) {
|
||||
return '';
|
||||
});
|
||||
|
||||
class ThemeModeState extends StateNotifier<bool> {
|
||||
final Ref _ref;
|
||||
|
||||
ThemeModeState(this._ref)
|
||||
: super(_ref.watch(hiveBoxSettings).get('isLight', defaultValue: true)!);
|
||||
|
||||
void setLightTheme() {
|
||||
state = true;
|
||||
_ref.watch(hiveBoxSettings).put('isLight', state);
|
||||
}
|
||||
|
||||
void setDarkTheme() {
|
||||
state = false;
|
||||
_ref.watch(hiveBoxSettings).put('isLight', state);
|
||||
}
|
||||
}
|
||||
26
lib/views/more/settings/settings_screen.dart
Normal file
26
lib/views/more/settings/settings_screen.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter/src/widgets/placeholder.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SettingsScreen extends StatelessWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Settings"),
|
||||
actions: [],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text("Appearance"),
|
||||
leading: const Icon(Icons.color_lens_rounded),
|
||||
onTap: () => context.push('/appearance')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,12 @@ import Foundation
|
|||
|
||||
import flutter_js
|
||||
import path_provider_foundation
|
||||
import rive_common
|
||||
import sqflite
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
}
|
||||
|
|
|
|||
24
pubspec.lock
24
pubspec.lock
|
|
@ -416,6 +416,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.2"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "927573f2e8a8d65c17931e21918ad0ab0666b1b636537de7c4932bdb487b190f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.3"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -720,6 +728,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
rive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rive
|
||||
sha256: "763b4915b5245428f1188d38c2ff8c26da83ca194d345daa319ca32d69ed670f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.3"
|
||||
rive_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive_common
|
||||
sha256: "12ea4a1ca1aa2ddeb2ef212afa20517d6c140a5deb32149c713912a7e6b7e26e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.3"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ dependencies:
|
|||
draggable_scrollbar: ^0.1.0
|
||||
grouped_list: ^5.1.2
|
||||
intl: ^0.18.0
|
||||
rive: ^0.10.3
|
||||
google_fonts: ^4.0.3
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
|
|
@ -81,7 +83,8 @@ flutter:
|
|||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
assets:
|
||||
- assets/
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@
|
|||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <rive_common/rive_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlutterJsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
|
||||
RivePluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("RivePlugin"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_js
|
||||
rive_common
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
Loading…
Reference in a new issue