Add desktop custom theme selection and download ui. As well fixed a couple bugs on the mobile version

This commit is contained in:
julian 2023-05-12 09:32:35 -06:00
parent d17da24607
commit 63fff1d644
14 changed files with 940 additions and 20 deletions

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.75 1V6.5H20.25L14.75 1ZM13.375 6.5V1H5.8125C4.6734 1 3.75 1.9234 3.75 3.0625V20.9375C3.75 22.0762 4.6734 23 5.8125 23H18.1875C19.3266 23 20.25 22.0766 20.25 20.9375V7.875H14.7887C13.9895 7.875 13.375 7.26055 13.375 6.5ZM16.1293 15.7855C15.966 16.0262 15.7039 16.125 15.4375 16.125C15.1711 16.125 14.9098 16.0243 14.7083 15.8229L13.0312 14.1441V18.5312C13.0312 19.1006 12.5693 19.5625 12 19.5625C11.4307 19.5625 10.9688 19.1006 10.9688 18.5312V14.1441L9.29168 15.8212C8.88885 16.224 8.23637 16.224 7.83332 15.8212C7.43027 15.4184 7.43049 14.7659 7.83332 14.3629L11.2708 10.9254C11.6737 10.5225 12.3261 10.5225 12.7292 10.9254L16.1667 14.3629C16.5676 14.7672 16.5676 15.4203 16.1293 15.7855Z" fill="#A9ACAC"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

3
assets/svg/file.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.16667 2.41699V4.83366C8.16667 6.03104 9.13595 7.00033 10.3333 7.00033H12.75V14.0003C12.75 14.3212 12.4875 14.5837 12.1667 14.5837H4.83333C4.51169 14.5837 4.25 14.3218 4.25 14.0003V3.00033C4.25 2.67805 4.51106 2.41699 4.83333 2.41699H8.16667Z" fill="#232323" stroke="#232323" stroke-width="2.5"/>
</svg>

After

Width:  |  Height:  |  Size: 411 B

View file

@ -34,7 +34,7 @@ class ManageThemesView extends ConsumerStatefulWidget {
class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
late bool _showThemes;
Future<List<StackThemeMetaData>> future = Future(() => []);
Future<List<StackThemeMetaData>> Function() future = () async => [];
void _onInstallPressed() {
showDialog<void>(
@ -46,6 +46,9 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
@override
void initState() {
_showThemes = ref.read(prefsChangeNotifierProvider).externalCalls;
if (_showThemes) {
future = ref.read(pThemeService).fetchThemes;
}
super.initState();
}
@ -121,7 +124,7 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
child: Text(
"You are using Incognito Mode. Please press the"
" button below to load available themes from our server"
" or upload a theme file manually from your device.",
" or install a theme file manually from your device.",
style: STextStyles.smallMed12(context),
),
),
@ -133,7 +136,7 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
onPressed: () {
setState(() {
_showThemes = true;
future = ref.watch(pThemeService).fetchThemes();
future = ref.watch(pThemeService).fetchThemes;
});
},
),
@ -147,8 +150,11 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
const SizedBox(
height: 16,
),
const Expanded(
child: _IncognitoInstalledThemes(),
Expanded(
child: IncognitoInstalledThemes(
cardWidth:
(MediaQuery.of(context).size.width - 48) / 2,
),
),
const SizedBox(
height: 16,
@ -164,7 +170,7 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: future,
future: future(),
builder: (
context,
AsyncSnapshot<List<StackThemeMetaData>> snapshot,
@ -177,6 +183,7 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
children: snapshot.data!
.map(
(e) => SizedBox(
key: Key("ManageThemesView_card_${e.id}_key"),
width: (MediaQuery.of(context).size.width - 48) / 2,
child: StackThemeCard(
data: e,
@ -200,16 +207,21 @@ class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
}
}
class _IncognitoInstalledThemes extends ConsumerStatefulWidget {
const _IncognitoInstalledThemes({Key? key}) : super(key: key);
class IncognitoInstalledThemes extends ConsumerStatefulWidget {
const IncognitoInstalledThemes({
Key? key,
required this.cardWidth,
}) : super(key: key);
final double cardWidth;
@override
ConsumerState<_IncognitoInstalledThemes> createState() =>
ConsumerState<IncognitoInstalledThemes> createState() =>
_IncognitoInstalledThemesState();
}
class _IncognitoInstalledThemesState
extends ConsumerState<_IncognitoInstalledThemes> {
extends ConsumerState<IncognitoInstalledThemes> {
late final StreamSubscription<void> _subscription;
List<Tuple2<String, String>> installedThemeIdNames = [];
@ -255,7 +267,8 @@ class _IncognitoInstalledThemesState
children: installedThemeIdNames
.map(
(e) => SizedBox(
width: (MediaQuery.of(context).size.width - 48) / 2,
key: Key("IncognitoInstalledThemes_card_${e.item1}_key"),
width: widget.cardWidth,
child: StackThemeCard(
data: StackThemeMetaData(
name: e.item2,

View file

@ -8,12 +8,14 @@ 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/providers/global/prefs_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
@ -33,6 +35,7 @@ class StackThemeCard extends ConsumerStatefulWidget {
}
class _StackThemeCardState extends ConsumerState<StackThemeCard> {
final isDesktop = Util.isDesktop;
late final StreamSubscription<void> _subscription;
late bool _hasTheme;
@ -170,6 +173,10 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
@override
Widget build(BuildContext context) {
return RoundedWhiteContainer(
radiusMultiplier: isDesktop ? 2.5 : 1,
borderColor: isDesktop
? Theme.of(context).extension<StackColors>()!.textSubtitle6
: null,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -259,13 +266,13 @@ class _StackThemeCardState extends ConsumerState<StackThemeCard> {
: CrossFadeState.showFirst,
firstChild: PrimaryButton(
label: "Download",
buttonHeight: ButtonHeight.l,
buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.l,
onPressed: _downloadPressed,
),
secondChild: SecondaryButton(
label: themeIsInUse ? "Theme is active" : "Remove from device",
label: themeIsInUse ? "Theme is active" : "Remove",
enabled: !themeIsInUse,
buttonHeight: ButtonHeight.l,
buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.l,
onPressed: _uninstallThemePressed,
),
),

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/language_settings/language_settings.dart';

View file

@ -1,8 +1,12 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_manage_themes.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
@ -200,6 +204,7 @@ class ThemeToggle extends ConsumerStatefulWidget {
}
class _ThemeToggle extends ConsumerState<ThemeToggle> {
late final StreamSubscription<void> _subscription;
late int _current;
List<Tuple3<String, String, String>> installedThemeIdNames = [];
@ -261,13 +266,24 @@ class _ThemeToggle extends ConsumerState<ThemeToggle> {
}
}
@override
void initState() {
void _updateInstalledList() {
installedThemeIdNames = ref
.read(pThemeService)
.installedThemes
.map((e) => Tuple3(e.themeId, e.name, e.assets.themePreview))
.toList();
}
void _manageThemesPressed() {
showDialog<void>(
context: context,
builder: (_) => const DesktopManageThemesDialog(),
);
}
@override
void initState() {
_updateInstalledList();
if (ref.read(prefsChangeNotifierProvider).enableSystemBrightness) {
_current = installedThemeIdNames.length;
@ -282,9 +298,26 @@ class _ThemeToggle extends ConsumerState<ThemeToggle> {
}
}
_subscription =
ref.read(mainDBProvider).isar.stackThemes.watchLazy().listen((_) {
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_updateInstalledList();
});
});
}
});
super.initState();
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Wrap(
@ -372,7 +405,56 @@ class _ThemeToggle extends ConsumerState<ThemeToggle> {
),
),
),
)
),
Container(
decoration: BoxDecoration(
border: Border.all(
width: 2.5,
color: Theme.of(context).extension<StackColors>()!.popupBG,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Semantics(
label: "Manage themes",
button: true,
excludeSemantics: true,
child: RawMaterialButton(
onPressed: _manageThemesPressed,
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
highlightElevation: 0,
fillColor: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Container(
color: Colors.transparent,
height: 160,
width: 200,
child: Center(
child: SvgPicture.asset(
Assets.svg.circlePlusFilled,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
width: 20,
height: 20,
),
),
),
),
),
),
),
],
);
}

View file

@ -0,0 +1,331 @@
import 'dart:typed_data';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:share_plus/share_plus.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/show_loading.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/desktop/outline_blue_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
class DesktopInstallTheme extends ConsumerStatefulWidget {
const DesktopInstallTheme({Key? key}) : super(key: key);
@override
ConsumerState<DesktopInstallTheme> createState() =>
_DesktopInstallThemeState();
}
class _DesktopInstallThemeState extends ConsumerState<DesktopInstallTheme> {
final _boxKey = GlobalKey(debugLabel: "selectThemeFileBoxKey");
XFile? _selectedFile;
bool? _installedState;
Size? _size;
bool _dragging = false;
Future<bool> _install() async {
try {
final timedFuture = Future<void>.delayed(const Duration(seconds: 2));
final installFuture = _selectedFile!.readAsBytes().then(
(fileBytes) => ref.read(pThemeService).install(
themeArchive: ByteData.view(
fileBytes.buffer,
),
),
);
// wait for at least 2 seconds to prevent annoying screen flashing
await Future.wait([
installFuture,
timedFuture,
]);
return true;
} catch (e, s) {
Logging.instance.log(
"Failed to install theme: $e\n$s",
level: LogLevel.Warning,
);
return false;
}
}
Future<void> _chooseFile() async {
try {
final result = await FilePicker.platform.pickFiles(
dialogTitle: "Choose theme file",
type: FileType.custom,
allowedExtensions: ["zip"],
lockParentWindow: true, // windows only
);
if (result != null && mounted) {
if (result.paths.isNotEmpty && result.paths.first != null) {
setState(() {
_selectedFile = XFile(result.paths.first!);
});
}
}
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Error);
}
}
void setBoxSize() {
if (_size == null) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
_size = _boxKey.currentContext?.size;
});
});
}
}
@override
void initState() {
setBoxSize();
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Text(
"Install theme file",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
const SizedBox(
height: 12,
),
DropTarget(
onDragDone: (detail) {
setState(() {
if (detail.files.isNotEmpty) {
_selectedFile = detail.files.first;
}
});
},
onDragEntered: (detail) {
setState(() {
_dragging = true;
});
},
onDragExited: (detail) {
setState(() {
_dragging = false;
});
},
child: RoundedContainer(
key: _boxKey,
height: _size?.height,
color: _dragging
? Theme.of(context).extension<StackColors>()!.textSubtitle6
: Theme.of(context).extension<StackColors>()!.popupBG,
borderColor:
Theme.of(context).extension<StackColors>()!.textSubtitle6,
child: _selectedFile == null
? Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 12,
),
SvgPicture.asset(
Assets.svg.fileUpload,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
width: 24,
height: 24,
),
const SizedBox(
height: 12,
),
Text(
"Drag and drop your file here",
style: STextStyles.fieldLabel(context),
),
const SizedBox(
height: 12,
),
OutlineBlueButton(
label: "Browse",
buttonHeight: ButtonHeight.s,
width: 140,
onPressed: _chooseFile,
),
const SizedBox(
height: 12,
),
],
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RoundedContainer(
padding: EdgeInsets.zero,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
width: 300,
child: Row(
children: [
const SizedBox(
width: 10,
),
SvgPicture.asset(
Assets.svg.file,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
width: 16,
height: 16,
),
const SizedBox(
width: 8,
),
Expanded(
child: Text(
_selectedFile!.name,
style: STextStyles.w500_14(context),
),
),
IconButton(
onPressed: () {
setState(() {
_selectedFile = null;
});
},
icon: SvgPicture.asset(
Assets.svg.circleX,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
width: 16,
height: 16,
),
),
],
),
)
],
),
),
),
if (_selectedFile != null)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 16, bottom: 20),
child: PrimaryButton(
label: "Install",
buttonHeight: ButtonHeight.s,
width: 140,
enabled: _installedState == null,
onPressed: () async {
final result = await showLoading(
whileFuture: _install(),
context: context,
message: "Installing ${_selectedFile!.name}...",
);
if (mounted) {
setState(() {
_installedState = result;
});
await Future<void>.delayed(
const Duration(milliseconds: 2000),
).then((_) {
if (mounted) {
setState(() {
_selectedFile = null;
_installedState = null;
});
}
});
}
},
),
),
],
),
if (_installedState == true)
RoundedContainer(
color:
Theme.of(context).extension<StackColors>()!.snackBarBackSuccess,
child: Row(
children: [
SvgPicture.asset(
Assets.svg.circleX,
color: Theme.of(context)
.extension<StackColors>()!
.snackBarTextSuccess,
width: 16,
height: 16,
),
const SizedBox(
width: 10,
),
Text(
"${_selectedFile?.name} theme installed",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarTextSuccess,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
if (_installedState == false)
RoundedContainer(
color:
Theme.of(context).extension<StackColors>()!.snackBarBackError,
child: Row(
children: [
SvgPicture.asset(
Assets.svg.circleX,
color: Theme.of(context)
.extension<StackColors>()!
.snackBarTextError,
width: 16,
height: 16,
),
const SizedBox(
width: 10,
),
Text(
"Failed to install ${_selectedFile?.name} theme",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarTextError,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
],
);
}
}

View file

@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_install_theme.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings/sub_widgets/desktop_themes_gallery.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/toggle.dart';
class DesktopManageThemesDialog extends ConsumerStatefulWidget {
const DesktopManageThemesDialog({Key? key}) : super(key: key);
@override
ConsumerState<DesktopManageThemesDialog> createState() =>
_DesktopManageThemesDialogState();
}
class _DesktopManageThemesDialogState
extends ConsumerState<DesktopManageThemesDialog> {
static const width = 580.0;
bool _isInstallFromFile = false;
@override
Widget build(BuildContext context) {
return DesktopDialog(
maxWidth: width,
maxHeight: 708,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Add more themes",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: SizedBox(
height: 56,
child: Toggle(
isOn: _isInstallFromFile,
onValueChanged: (value) {
if (value != _isInstallFromFile) {
setState(() {
_isInstallFromFile = value;
});
}
},
onColor: Theme.of(context)
.extension<StackColors>()!
.rateTypeToggleDesktopColorOn,
offColor: Theme.of(context)
.extension<StackColors>()!
.rateTypeToggleDesktopColorOff,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onText: "Theme gallery",
offText: "Install file",
),
),
),
const SizedBox(
height: 16,
),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
return AnimatedCrossFade(
crossFadeState: _isInstallFromFile
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: const Duration(
milliseconds: 300,
),
firstChild: SizedBox(
height: constraints.maxHeight,
child: const DesktopThemeGallery(
dialogWidth: width,
),
),
secondChild: SizedBox(
height: constraints.maxHeight,
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 32),
child: DesktopInstallTheme(),
),
),
);
},
),
),
const SizedBox(
height: 32,
),
],
),
);
}
}

View file

@ -0,0 +1,153 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/appearance_settings/manage_themes.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart';
import 'package:stackwallet/providers/global/prefs_provider.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/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopThemeGallery extends ConsumerStatefulWidget {
const DesktopThemeGallery({
Key? key,
required this.dialogWidth,
}) : super(key: key);
final double dialogWidth;
@override
ConsumerState<DesktopThemeGallery> createState() =>
_DesktopThemeGalleryState();
}
class _DesktopThemeGalleryState extends ConsumerState<DesktopThemeGallery> {
late bool _showThemes;
Future<List<StackThemeMetaData>> Function() future = () async => [];
@override
void initState() {
_showThemes = ref.read(prefsChangeNotifierProvider).externalCalls;
if (_showThemes) {
future = ref.read(pThemeService).fetchThemes;
}
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Theme Gallery",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
],
),
const SizedBox(
height: 12,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: SingleChildScrollView(
child: _showThemes
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: future(),
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(
key: Key(
"_DesktopThemeGalleryState_card_${e.id}_key"),
width:
(widget.dialogWidth - 64 - 32) / 3,
child: StackThemeCard(
data: e,
),
),
)
.toList(),
);
} else {
return const Center(
child: LoadingIndicator(
width: 200,
),
);
}
},
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RoundedWhiteContainer(
borderColor: Theme.of(context)
.extension<StackColors>()!
.textSubtitle6,
child: Text(
"You are using Incognito Mode."
" Please press the button below to load "
"available themes from our server or install a "
"theme file manually from your computer.",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
),
const SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
PrimaryButton(
label: "Load themes",
width: 140,
buttonHeight: ButtonHeight.s,
onPressed: () {
setState(() {
_showThemes = true;
future = ref.read(pThemeService).fetchThemes;
});
},
),
],
),
const SizedBox(
height: 20,
),
IncognitoInstalledThemes(
cardWidth: (widget.dialogWidth - 64 - 32) / 3,
),
],
),
),
),
),
],
);
}
}

View file

@ -101,7 +101,6 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_set
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart';
import 'package:stackwallet/pages/stack_privacy_calls.dart';
import 'package:stackwallet/widgets/choose_coin_view.dart';
import 'package:stackwallet/pages/token_view/my_tokens_view.dart';
import 'package:stackwallet/pages/token_view/token_contract_details_view.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
@ -136,7 +135,7 @@ import 'package:stackwallet/pages_desktop_specific/password/forgot_password_desk
import 'package:stackwallet/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart';
import 'package:stackwallet/pages_desktop_specific/settings/desktop_settings_view.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/currency_settings/currency_settings.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/desktop_about_view.dart';
@ -151,6 +150,7 @@ import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/widgets/choose_coin_view.dart';
import 'package:tuple/tuple.dart';
class RouteGenerator {

View file

@ -1749,6 +1749,30 @@ class StackColors extends ThemeExtension<StackColors> {
),
);
ButtonStyle? getOutlineBlueButtonStyle(BuildContext context) =>
Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor: MaterialStateProperty.all<Color>(
Colors.transparent,
),
side: MaterialStateProperty.all<BorderSide>(
BorderSide(
color: customTextButtonEnabledText,
),
),
);
ButtonStyle? getOutlineBlueButtonDisabledStyle(BuildContext context) =>
Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor: MaterialStateProperty.all<Color>(
Colors.transparent,
),
side: MaterialStateProperty.all<BorderSide>(
BorderSide(
color: customTextButtonDisabledText,
),
),
);
ButtonStyle? getSecondaryEnabledButtonStyle(BuildContext context) =>
Theme.of(context).textButtonTheme.style?.copyWith(
backgroundColor: MaterialStateProperty.all<Color>(

View file

@ -182,6 +182,8 @@ class _SVG {
String get tokens => "assets/svg/tokens.svg";
String get circlePlusDark => "assets/svg/circle-plus.svg";
String get creditCard => "assets/svg/cc.svg";
String get file => "assets/svg/file.svg";
String get fileUpload => "assets/svg/file-upload.svg";
String get ellipse1 => "assets/svg/Ellipse-43.svg";
String get ellipse2 => "assets/svg/Ellipse-42.svg";

View file

@ -0,0 +1,188 @@
import 'package:flutter/material.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/custom_text_button.dart';
export 'package:stackwallet/widgets/desktop/custom_text_button.dart';
class OutlineBlueButton extends StatelessWidget {
const OutlineBlueButton({
Key? key,
this.width,
this.height,
this.label,
this.icon,
this.onPressed,
this.enabled = true,
this.buttonHeight,
this.iconSpacing = 10,
}) : super(key: key);
final double? width;
final double? height;
final String? label;
final VoidCallback? onPressed;
final bool enabled;
final Widget? icon;
final ButtonHeight? buttonHeight;
final double? iconSpacing;
TextStyle getStyle(bool isDesktop, BuildContext context) {
if (isDesktop) {
if (buttonHeight == null) {
return enabled
? STextStyles.desktopButtonEnabled(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText,
)
: STextStyles.desktopButtonDisabled(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.customTextButtonDisabledText,
);
}
switch (buttonHeight!) {
case ButtonHeight.xxs:
case ButtonHeight.xs:
case ButtonHeight.s:
return STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: enabled
? Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText
: Theme.of(context)
.extension<StackColors>()!
.customTextButtonDisabledText,
);
case ButtonHeight.m:
case ButtonHeight.l:
return STextStyles.desktopTextExtraSmall(context).copyWith(
color: enabled
? Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText
: Theme.of(context)
.extension<StackColors>()!
.customTextButtonDisabledText,
);
case ButtonHeight.xl:
case ButtonHeight.xxl:
return enabled
? STextStyles.desktopButtonEnabled(context)
: STextStyles.desktopButtonDisabled(context);
}
} else {
if (buttonHeight == ButtonHeight.l) {
return STextStyles.button(context).copyWith(
fontSize: 10,
color: enabled
? Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText
: Theme.of(context)
.extension<StackColors>()!
.customTextButtonDisabledText,
);
}
return STextStyles.button(context).copyWith(
color: enabled
? Theme.of(context)
.extension<StackColors>()!
.customTextButtonEnabledText
: Theme.of(context)
.extension<StackColors>()!
.customTextButtonDisabledText,
);
}
}
double? _getHeight() {
if (buttonHeight == null) {
return height;
}
if (Util.isDesktop) {
switch (buttonHeight!) {
case ButtonHeight.xxs:
return 32;
case ButtonHeight.xs:
return 37;
case ButtonHeight.s:
return 40;
case ButtonHeight.m:
return 48;
case ButtonHeight.l:
return 56;
case ButtonHeight.xl:
return 70;
case ButtonHeight.xxl:
return 96;
}
} else {
switch (buttonHeight!) {
case ButtonHeight.xxs:
case ButtonHeight.xs:
case ButtonHeight.s:
case ButtonHeight.m:
return 28;
case ButtonHeight.l:
return 30;
case ButtonHeight.xl:
return 46;
case ButtonHeight.xxl:
return 56;
}
}
}
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
return CustomTextButtonBase(
height: _getHeight(),
width: width,
textButton: TextButton(
onPressed: enabled ? onPressed : null,
style: enabled
? Theme.of(context)
.extension<StackColors>()!
.getOutlineBlueButtonStyle(context)
: Theme.of(context)
.extension<StackColors>()!
.getOutlineBlueButtonDisabledStyle(context),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null) icon!,
if (icon != null && label != null)
SizedBox(
width: iconSpacing,
),
if (label != null)
Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
label!,
style: getStyle(isDesktop, context),
),
if (buttonHeight != null && buttonHeight == ButtonHeight.s)
const SizedBox(
height: 2,
),
],
),
],
),
),
);
}
}

View file

@ -329,6 +329,8 @@ flutter:
- assets/svg/framed-gear.svg
- assets/svg/list-ul.svg
- assets/svg/cc.svg
- assets/svg/file.svg
- assets/svg/file-upload.svg
- assets/svg/trocador_rating_a.svg
- assets/svg/trocador_rating_b.svg
- assets/svg/trocador_rating_c.svg