various fixes, layout tweaks, and refactoring

This commit is contained in:
julian 2023-05-10 16:49:56 -06:00
parent 6ed6ffb18b
commit 6990d60b9b
4 changed files with 234 additions and 100 deletions

View file

@ -1645,7 +1645,9 @@ class StackTheme {
backgroundInt: parseColor(json["colors"]["background"] as String),
backgroundAppBarInt:
parseColor(json["colors"]["background_app_bar"] as String),
gradientBackgroundString: json["colors"]["gradients"] as String?,
gradientBackgroundString: json["colors"]["gradients"] != null
? jsonEncode(json["colors"]["gradients"])
: null,
standardBoxShadowString:
jsonEncode(json["colors"]["box_shadows"]["standard"] as Map),
homeViewButtonBarBoxShadowString:

View file

@ -1,24 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
class ManageThemesView extends StatefulWidget {
class ManageThemesView extends ConsumerStatefulWidget {
const ManageThemesView({Key? key}) : super(key: key);
static const String routeName = "/manageThemes";
@override
State<ManageThemesView> createState() => _ManageThemesViewState();
ConsumerState<ManageThemesView> createState() => _ManageThemesViewState();
}
class _ManageThemesViewState extends State<ManageThemesView> {
class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
@override
Widget build(BuildContext context) {
return ConditionalParent(
@ -36,105 +37,64 @@ class _ManageThemesViewState extends State<ManageThemesView> {
style: STextStyles.navBarTitle(context),
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: IntrinsicHeight(
child: child,
),
),
),
),
child: child,
),
Padding(
padding: const EdgeInsets.all(16),
child: SecondaryButton(
label: "Install theme file",
onPressed: () {},
),
),
],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 16,
),
GridView.builder(
shrinkWrap: true,
primary: false,
itemCount: 100,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 2 / 2.7,
),
itemBuilder: (_, index) {
return StackThemeCard(
name: index.toString(),
size: "lol GB",
);
FutureBuilder(
future: ref.watch(pThemeService).fetchThemes(),
builder: (
context,
AsyncSnapshot<List<StackThemeMetaData>> snapshot,
) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return Wrap(
spacing: 16,
runSpacing: 16,
children: snapshot.data!
.map(
(e) => SizedBox(
width: (MediaQuery.of(context).size.width - 48) / 2,
child: StackThemeCard(
data: e,
),
),
)
.toList(),
);
} else {
return Center(
child: LoadingIndicator(
width: (MediaQuery.of(context).size.width - 48) / 2,
),
);
}
},
),
const SizedBox(
height: 28,
),
SecondaryButton(
label: "Install theme file",
onPressed: () {},
),
const SizedBox(
height: 16,
),
],
),
);
}
}
class StackThemeCard extends StatefulWidget {
const StackThemeCard({
Key? key,
required this.name,
required this.size,
}) : super(key: key);
final String name;
final String size;
@override
State<StackThemeCard> createState() => _StackThemeCardState();
}
class _StackThemeCardState extends State<StackThemeCard> {
String buttonLabel = "Download";
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
child: Column(
children: [
const Padding(
padding: EdgeInsets.symmetric(
horizontal: 18,
),
child: AspectRatio(
aspectRatio: 1,
child: RoundedContainer(
color: Colors.grey,
radiusMultiplier: 100,
),
),
),
const SizedBox(
height: 12,
),
Text(
widget.name,
),
const SizedBox(
height: 6,
),
Text(
widget.size,
),
const Spacer(),
PrimaryButton(
label: buttonLabel,
buttonHeight: ButtonHeight.l,
onPressed: () {},
),
],
),
);

View file

@ -0,0 +1,167 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class StackThemeCard extends ConsumerStatefulWidget {
const StackThemeCard({
Key? key,
required this.data,
}) : super(key: key);
final StackThemeMetaData data;
@override
ConsumerState<StackThemeCard> createState() => _StackThemeCardState();
}
class _StackThemeCardState extends ConsumerState<StackThemeCard> {
String buttonLabel = "Download";
late bool _hasTheme;
Future<bool> _downloadAndInstall() async {
final service = ref.read(pThemeService);
try {
final data = await service.fetchTheme(
themeMetaData: widget.data,
);
await service.install(themeArchive: data);
return true;
} catch (e, s) {
Logging.instance.log(
"Failed _downloadAndInstall of ${widget.data.id}: $e\n$s",
level: LogLevel.Warning,
);
return false;
}
}
Future<void> _downloadPressed() async {
final result = await showLoading(
whileFuture: _downloadAndInstall(),
context: context,
message: "Downloading and installing theme...",
);
if (mounted) {
final message = result
? "${widget.data.name} theme installed!"
: "Failed to install ${widget.data.name} theme";
await showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: message,
onOkPressed: (_) {
setState(() {
_hasTheme = result;
});
},
),
);
}
}
late final StreamSubscription<void> _subscription;
@override
void initState() {
_hasTheme = ref
.read(mainDBProvider)
.isar
.stackThemes
.where()
.themeIdEqualTo(widget.data.id)
.countSync() >
0;
_subscription = ref
.read(mainDBProvider)
.isar
.stackThemes
.watchLazy()
.listen((event) async {
final hasTheme = (await ref
.read(mainDBProvider)
.isar
.stackThemes
.where()
.themeIdEqualTo(widget.data.id)
.count()) >
0;
if (_hasTheme != hasTheme && mounted) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
_hasTheme = hasTheme;
});
});
}
});
_subscription.resume();
super.initState();
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18,
),
child: AspectRatio(
aspectRatio: 1,
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
widget.data.previewImageUrl,
),
),
),
),
const SizedBox(
height: 12,
),
Text(
widget.data.name,
),
const SizedBox(
height: 6,
),
Text(
widget.data.size,
),
const SizedBox(
height: 12,
),
PrimaryButton(
label: buttonLabel,
enabled: !_hasTheme,
buttonHeight: ButtonHeight.l,
onPressed: _downloadPressed,
),
],
),
);
}
}

View file

@ -117,7 +117,7 @@ class ThemeService {
}
}
Future<List<StackThemeMetaData>> fetchThemeList() async {
Future<List<StackThemeMetaData>> fetchThemes() async {
try {
final response = await get(Uri.parse("$baseServerUrl/themes"));
@ -125,6 +125,7 @@ class ThemeService {
final result = List<Map<String, dynamic>>.from(jsonList)
.map((e) => StackThemeMetaData.fromMap(e))
.where((e) => e.id != "light" && e.id != "dark")
.toList();
return result;
@ -178,12 +179,14 @@ class StackThemeMetaData {
final String name;
final String id;
final String sha256;
final String size;
final String previewImageUrl;
StackThemeMetaData({
required this.name,
required this.id,
required this.sha256,
required this.size,
required this.previewImageUrl,
});
@ -193,6 +196,7 @@ class StackThemeMetaData {
name: map["name"] as String,
id: map["id"] as String,
sha256: map["sha256"] as String,
size: map["size"] as String,
previewImageUrl: map["previewImageUrl"] as String,
);
} catch (e, s) {
@ -210,6 +214,7 @@ class StackThemeMetaData {
"name: $name, "
"id: $id, "
"sha256: $sha256, "
"size: $size, "
"previewImageUrl: $previewImageUrl"
")";
}