mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-24 04:19:23 +00:00
commit
cb4ba48d5d
17 changed files with 1154 additions and 160 deletions
|
@ -76,11 +76,17 @@ void main() async {
|
|||
Util.libraryPath = await getLibraryDirectory();
|
||||
}
|
||||
|
||||
Screen? screen;
|
||||
if (Platform.isLinux || Util.isDesktop) {
|
||||
screen = await getCurrentScreen();
|
||||
Util.screenWidth = screen?.frame.width;
|
||||
}
|
||||
|
||||
if (Util.isDesktop) {
|
||||
setWindowTitle('Stack Wallet');
|
||||
setWindowMinSize(const Size(1220, 100));
|
||||
setWindowMaxSize(Size.infinite);
|
||||
final screen = await getCurrentScreen();
|
||||
|
||||
final screenHeight = screen?.frame.height;
|
||||
if (screenHeight != null) {
|
||||
// starting to height be 3/4 screen height or 900, whichever is smaller
|
||||
|
@ -312,17 +318,17 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
final colorScheme = DB.instance
|
||||
.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme") as String?;
|
||||
|
||||
ThemeType themeType;
|
||||
StackColorTheme colorTheme;
|
||||
switch (colorScheme) {
|
||||
case "dark":
|
||||
themeType = ThemeType.dark;
|
||||
colorTheme = DarkColors();
|
||||
break;
|
||||
case "oceanBreeze":
|
||||
themeType = ThemeType.oceanBreeze;
|
||||
colorTheme = OceanBreezeColors();
|
||||
break;
|
||||
case "light":
|
||||
default:
|
||||
themeType = ThemeType.light;
|
||||
colorTheme = LightColors();
|
||||
}
|
||||
loadingCompleter = Completer();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
@ -333,11 +339,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
ref.read(colorThemeProvider.state).state =
|
||||
StackColors.fromStackColorTheme(themeType == ThemeType.dark
|
||||
? DarkColors()
|
||||
: (themeType == ThemeType.light
|
||||
? LightColors()
|
||||
: OceanBreezeColors()));
|
||||
StackColors.fromStackColorTheme(colorTheme);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
// fetch open file if it exists
|
||||
|
|
|
@ -23,6 +23,8 @@ import 'package:stackwallet/utilities/util.dart';
|
|||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart'
|
||||
as datePicker;
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
|
@ -152,10 +154,10 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
await Future<void>.delayed(const Duration(milliseconds: 125));
|
||||
}
|
||||
|
||||
final date = await showRoundedDatePicker(
|
||||
final date = await datePicker.showRoundedDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
height: height / 3.2,
|
||||
height: height * 0.5,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Util.createMaterialColor(fetchedColor),
|
||||
),
|
||||
|
|
|
@ -25,6 +25,9 @@ import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
|||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
|
||||
import '../../../../../pages_desktop_specific/home/desktop_home_view.dart';
|
||||
import '../../../../../pages_desktop_specific/home/desktop_menu.dart';
|
||||
import '../../../../../providers/desktop/current_desktop_menu_item.dart';
|
||||
import '../../../../../widgets/desktop/primary_button.dart';
|
||||
|
||||
class StackRestoreProgressView extends ConsumerStatefulWidget {
|
||||
|
@ -685,7 +688,19 @@ class _StackRestoreProgressViewState
|
|||
enabled: true,
|
||||
label: "Done",
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
DesktopMenuItemId keyID =
|
||||
DesktopMenuItemId.myStack;
|
||||
|
||||
ref
|
||||
.read(currentDesktopMenuItemProvider
|
||||
.state)
|
||||
.state = keyID;
|
||||
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.popUntil(
|
||||
ModalRoute.withName(
|
||||
DesktopHomeView.routeName),
|
||||
);
|
||||
},
|
||||
)
|
||||
: SecondaryButton(
|
||||
|
|
|
@ -49,6 +49,8 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
bool _hovering = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final coin = ref.watch(managerProvider.select((value) => value.coin));
|
||||
|
@ -59,7 +61,48 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
|
|||
condition: Util.isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
onEnter: (_) {
|
||||
setState(() {
|
||||
_hovering = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
setState(() {
|
||||
_hovering = false;
|
||||
});
|
||||
},
|
||||
child: AnimatedScale(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
scale: _hovering ? 1.05 : 1,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
decoration: _hovering
|
||||
? BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
boxShadow: [
|
||||
Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.standardBoxShadow,
|
||||
Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.standardBoxShadow,
|
||||
Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.standardBoxShadow,
|
||||
],
|
||||
)
|
||||
: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
|
|
|
@ -9,12 +9,19 @@ import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_no
|
|||
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_about_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_support_view.dart';
|
||||
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
|
||||
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
|
||||
import 'package:stackwallet/providers/global/notifications_provider.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
|
||||
final currentWalletIdProvider = StateProvider<String?>((_) => null);
|
||||
|
||||
class DesktopHomeView extends ConsumerStatefulWidget {
|
||||
const DesktopHomeView({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -25,12 +32,25 @@ class DesktopHomeView extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
|
||||
final Map<DesktopMenuItemId, Widget> contentViews = {
|
||||
DesktopMenuItemId.myStack: const Navigator(
|
||||
key: Key("desktopStackHomeKey"),
|
||||
final GlobalKey key = GlobalKey<NavigatorState>();
|
||||
late final Navigator myStackViewNav;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
myStackViewNav = Navigator(
|
||||
key: key,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
initialRoute: MyStackView.routeName,
|
||||
),
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
final Map<DesktopMenuItemId, Widget> contentViews = {
|
||||
DesktopMenuItemId.myStack: Container(
|
||||
// key: Key("desktopStackHomeKey"),
|
||||
// onGenerateRoute: RouteGenerator.generateRoute,
|
||||
// initialRoute: MyStackView.routeName,
|
||||
),
|
||||
DesktopMenuItemId.exchange: const Navigator(
|
||||
key: Key("desktopExchangeHomeKey"),
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
|
@ -63,7 +83,30 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
|
|||
),
|
||||
};
|
||||
|
||||
DesktopMenuItemId prev = DesktopMenuItemId.myStack;
|
||||
|
||||
void onMenuSelectionWillChange(DesktopMenuItemId newKey) {
|
||||
if (prev == DesktopMenuItemId.myStack && prev == newKey) {
|
||||
Navigator.of(key.currentContext!)
|
||||
.popUntil(ModalRoute.withName(MyStackView.routeName));
|
||||
if (ref.read(currentWalletIdProvider.state).state != null) {
|
||||
final managerProvider = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManagerProvider(ref.read(currentWalletIdProvider.state).state!);
|
||||
if (ref.read(managerProvider).shouldAutoSync) {
|
||||
ref.read(managerProvider).shouldAutoSync = false;
|
||||
}
|
||||
ref.read(transactionFilterProvider.state).state = null;
|
||||
if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled &&
|
||||
ref.read(prefsChangeNotifierProvider).backupFrequencyType ==
|
||||
BackupFrequencyType.afterClosingAWallet) {
|
||||
ref.read(autoSWBServiceProvider).doBackup();
|
||||
}
|
||||
ref.read(managerProvider.notifier).isActiveWallet = false;
|
||||
}
|
||||
}
|
||||
prev = newKey;
|
||||
|
||||
// check for unread notifications and refresh provider before
|
||||
// showing notifications view
|
||||
if (newKey == DesktopMenuItemId.notifications) {
|
||||
|
@ -111,9 +154,25 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
|
|||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
),
|
||||
Expanded(
|
||||
child: contentViews[
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state]!,
|
||||
child: IndexedStack(
|
||||
index: ref
|
||||
.watch(currentDesktopMenuItemProvider.state)
|
||||
.state
|
||||
.index >
|
||||
0
|
||||
? 1
|
||||
: 0,
|
||||
children: [
|
||||
myStackViewNav,
|
||||
contentViews[
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state]!,
|
||||
],
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: contentViews[
|
||||
// ref.watch(currentDesktopMenuItemProvider.state).state]!,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:stackwallet/providers/providers.dart';
|
|||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/desktop/living_stack_icon.dart';
|
||||
|
||||
enum DesktopMenuItemId {
|
||||
myStack,
|
||||
|
@ -38,6 +39,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
static const expandedWidth = 225.0;
|
||||
static const minimizedWidth = 72.0;
|
||||
|
||||
final Duration duration = const Duration(milliseconds: 250);
|
||||
late final List<DMIController> controllers;
|
||||
|
||||
double _width = expandedWidth;
|
||||
|
||||
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
|
||||
|
@ -49,45 +53,84 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
}
|
||||
|
||||
void toggleMinimize() {
|
||||
final expanded = _width == expandedWidth;
|
||||
|
||||
for (var e in controllers) {
|
||||
e.toggle?.call();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_width = _width == expandedWidth ? minimizedWidth : expandedWidth;
|
||||
_width = expanded ? minimizedWidth : expandedWidth;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controllers = [
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
DMIController(),
|
||||
];
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var e in controllers) {
|
||||
e.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
child: SizedBox(
|
||||
child: AnimatedContainer(
|
||||
width: _width,
|
||||
duration: duration,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: _width == expandedWidth ? 22 : 25,
|
||||
const SizedBox(
|
||||
height: 25,
|
||||
),
|
||||
SizedBox(
|
||||
AnimatedContainer(
|
||||
duration: duration,
|
||||
width: _width == expandedWidth ? 70 : 32,
|
||||
height: _width == expandedWidth ? 70 : 32,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.stackIcon(context),
|
||||
child: LivingStackIcon(
|
||||
onPressed: toggleMinimize,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
_width == expandedWidth ? "Stack Wallet" : "",
|
||||
style: STextStyles.desktopH2(context).copyWith(
|
||||
fontSize: 18,
|
||||
height: 23.4 / 18,
|
||||
AnimatedOpacity(
|
||||
duration: duration,
|
||||
opacity: _width == expandedWidth ? 1 : 0,
|
||||
child: SizedBox(
|
||||
height: 28,
|
||||
child: Text(
|
||||
"Stack Wallet",
|
||||
style: STextStyles.desktopH2(context).copyWith(
|
||||
fontSize: 18,
|
||||
height: 23.4 / 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 60,
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
child: AnimatedContainer(
|
||||
duration: duration,
|
||||
width: _width == expandedWidth
|
||||
? _width - 32 // 16 padding on either side
|
||||
: _width - 16, // 8 padding on either side
|
||||
|
@ -95,6 +138,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.walletDesktop,
|
||||
width: 20,
|
||||
|
@ -113,15 +157,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "My Stack",
|
||||
value: DesktopMenuItemId.myStack,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[0],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.exchangeDesktop,
|
||||
width: 20,
|
||||
|
@ -140,15 +183,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Exchange",
|
||||
value: DesktopMenuItemId.exchange,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[1],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
ref.watch(notificationsProvider.select(
|
||||
(value) => value.hasUnreadNotifications))
|
||||
|
@ -174,15 +216,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Notifications",
|
||||
value: DesktopMenuItemId.notifications,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[2],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.addressBookDesktop,
|
||||
width: 20,
|
||||
|
@ -201,15 +242,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Address Book",
|
||||
value: DesktopMenuItemId.addressBook,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[3],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.gear,
|
||||
width: 20,
|
||||
|
@ -228,15 +268,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Settings",
|
||||
value: DesktopMenuItemId.settings,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[4],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.messageQuestion,
|
||||
width: 20,
|
||||
|
@ -255,15 +294,14 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Support",
|
||||
value: DesktopMenuItemId.support,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[5],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.aboutDesktop,
|
||||
width: 20,
|
||||
|
@ -282,13 +320,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "About",
|
||||
value: DesktopMenuItemId.about,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[6],
|
||||
),
|
||||
const Spacer(),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
labelLength: 123,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.exitDesktop,
|
||||
width: 20,
|
||||
|
@ -300,13 +338,11 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
label: "Exit",
|
||||
value: 7,
|
||||
group:
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||
onChanged: (_) {
|
||||
// todo: save stuff/ notify before exit?
|
||||
exit(0);
|
||||
},
|
||||
iconOnly: _width == minimizedWidth,
|
||||
controller: controllers[7],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,27 +1,96 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
class DesktopMenuItem<T> extends StatelessWidget {
|
||||
class DMIController {
|
||||
VoidCallback? toggle;
|
||||
void dispose() {
|
||||
toggle = null;
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopMenuItem<T> extends ConsumerStatefulWidget {
|
||||
const DesktopMenuItem({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.group,
|
||||
required this.onChanged,
|
||||
required this.iconOnly,
|
||||
required this.duration,
|
||||
this.labelLength = 125,
|
||||
this.controller,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget icon;
|
||||
final String label;
|
||||
final T value;
|
||||
final T group;
|
||||
final void Function(T) onChanged;
|
||||
final bool iconOnly;
|
||||
final Duration duration;
|
||||
final double labelLength;
|
||||
final DMIController? controller;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopMenuItem<T>> createState() => _DesktopMenuItemState<T>();
|
||||
}
|
||||
|
||||
class _DesktopMenuItemState<T> extends ConsumerState<DesktopMenuItem<T>>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final Widget icon;
|
||||
late final String label;
|
||||
late final T value;
|
||||
late final void Function(T) onChanged;
|
||||
late final Duration duration;
|
||||
late final double labelLength;
|
||||
|
||||
late final DMIController? controller;
|
||||
|
||||
late final AnimationController animationController;
|
||||
|
||||
bool _iconOnly = false;
|
||||
|
||||
void toggle() {
|
||||
setState(() {
|
||||
_iconOnly = !_iconOnly;
|
||||
});
|
||||
if (_iconOnly) {
|
||||
animationController.reverse();
|
||||
} else {
|
||||
animationController.forward();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
icon = widget.icon;
|
||||
label = widget.label;
|
||||
value = widget.value;
|
||||
onChanged = widget.onChanged;
|
||||
duration = widget.duration;
|
||||
labelLength = widget.labelLength;
|
||||
controller = widget.controller;
|
||||
|
||||
controller?.toggle = toggle;
|
||||
animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: duration,
|
||||
)..forward();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller?.dispose();
|
||||
animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final group = ref.watch(currentDesktopMenuItemProvider.state).state;
|
||||
debugPrint("============ value:$value ============ group:$group");
|
||||
return TextButton(
|
||||
style: value == group
|
||||
? Theme.of(context)
|
||||
|
@ -34,26 +103,42 @@ class DesktopMenuItem<T> extends StatelessWidget {
|
|||
onChanged(value);
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: iconOnly ? 0 : 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
iconOnly ? MainAxisAlignment.center : MainAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: duration,
|
||||
width: _iconOnly ? 0 : 16,
|
||||
),
|
||||
icon,
|
||||
if (!iconOnly)
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
if (!iconOnly)
|
||||
Text(
|
||||
label,
|
||||
style: value == group
|
||||
? STextStyles.desktopMenuItemSelected(context)
|
||||
: STextStyles.desktopMenuItem(context),
|
||||
AnimatedOpacity(
|
||||
duration: duration,
|
||||
opacity: _iconOnly ? 0 : 1.0,
|
||||
child: SizeTransition(
|
||||
sizeFactor: animationController,
|
||||
axis: Axis.horizontal,
|
||||
axisAlignment: -1,
|
||||
child: SizedBox(
|
||||
width: labelLength,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: value == group
|
||||
? STextStyles.desktopMenuItemSelected(context)
|
||||
: STextStyles.desktopMenuItem(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
|
||||
|
||||
class CoinWalletsTable extends ConsumerWidget {
|
||||
|
@ -24,8 +26,10 @@ class CoinWalletsTable extends ConsumerWidget {
|
|||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 16,
|
||||
// horizontal: 20,
|
||||
// vertical: 16,
|
||||
horizontal: 6,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
|
@ -36,14 +40,29 @@ class CoinWalletsTable extends ConsumerWidget {
|
|||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
WalletInfoRow(
|
||||
walletId: walletIds[i],
|
||||
onPressed: () async {
|
||||
await Navigator.of(context).pushNamed(
|
||||
DesktopWalletView.routeName,
|
||||
arguments: walletIds[i],
|
||||
);
|
||||
},
|
||||
Stack(
|
||||
children: [
|
||||
WalletInfoRow(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 10,
|
||||
),
|
||||
walletId: walletIds[i],
|
||||
),
|
||||
Positioned.fill(
|
||||
child: WalletRowHoverOverlay(
|
||||
onPressed: () async {
|
||||
ref.read(currentWalletIdProvider.state).state =
|
||||
walletIds[i];
|
||||
|
||||
await Navigator.of(context).pushNamed(
|
||||
DesktopWalletView.routeName,
|
||||
arguments: walletIds[i],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -53,3 +72,55 @@ class CoinWalletsTable extends ConsumerWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WalletRowHoverOverlay extends StatefulWidget {
|
||||
const WalletRowHoverOverlay({
|
||||
Key? key,
|
||||
required this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
State<WalletRowHoverOverlay> createState() => _WalletRowHoverOverlayState();
|
||||
}
|
||||
|
||||
class _WalletRowHoverOverlayState extends State<WalletRowHoverOverlay> {
|
||||
late final VoidCallback onPressed;
|
||||
|
||||
bool _hovering = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
onPressed = widget.onPressed;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) {
|
||||
setState(() {
|
||||
_hovering = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
setState(() {
|
||||
_hovering = false;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
opacity: _hovering ? 0.1 : 0,
|
||||
child: RoundedContainer(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,13 +81,13 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
|||
// disable auto sync if it was enabled only when loading wallet
|
||||
ref.read(managerProvider).shouldAutoSync = false;
|
||||
}
|
||||
ref.read(managerProvider.notifier).isActiveWallet = false;
|
||||
ref.read(transactionFilterProvider.state).state = null;
|
||||
if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled &&
|
||||
ref.read(prefsChangeNotifierProvider).backupFrequencyType ==
|
||||
BackupFrequencyType.afterClosingAWallet) {
|
||||
unawaited(ref.read(autoSWBServiceProvider).doBackup());
|
||||
}
|
||||
ref.read(managerProvider.notifier).isActiveWallet = false;
|
||||
}
|
||||
|
||||
void _loadCNData() {
|
||||
|
|
|
@ -95,7 +95,8 @@ class _DeleteWalletKeysPopup extends ConsumerState<DeleteWalletKeysPopup> {
|
|||
horizontal: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
|
||||
"Please write down your recovery phrase in the correct order and "
|
||||
"save it to keep your funds secure. You will be shown your recovery phrase on the next screen.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
abstract class Util {
|
||||
static Directory? libraryPath;
|
||||
static double? screenWidth;
|
||||
|
||||
static bool get isDesktop {
|
||||
if(Platform.isIOS && libraryPath != null && !libraryPath!.path.contains("/var/mobile/")){
|
||||
// special check for running on linux based phones
|
||||
if (Platform.isLinux && screenWidth != null && screenWidth! < 800) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// special check for running under ipad mode in macos
|
||||
if (Platform.isIOS &&
|
||||
libraryPath != null &&
|
||||
!libraryPath!.path.contains("/var/mobile/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Platform.isLinux || Platform.isMacOS || Platform.isWindows;
|
||||
}
|
||||
|
||||
|
|
54
lib/widgets/desktop/living_stack_icon.dart
Normal file
54
lib/widgets/desktop/living_stack_icon.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
|
||||
class LivingStackIcon extends StatefulWidget {
|
||||
const LivingStackIcon({Key? key, this.onPressed,}) : super(key: key);
|
||||
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
State<LivingStackIcon> createState() => _LivingStackIconState();
|
||||
}
|
||||
|
||||
class _LivingStackIconState extends State<LivingStackIcon> {
|
||||
bool _hovering = false;
|
||||
|
||||
late final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
onPressed = widget.onPressed;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 76,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) {
|
||||
setState(() {
|
||||
_hovering = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
setState(() {
|
||||
_hovering = false;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () => onPressed?.call(),
|
||||
child: AnimatedScale(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
scale: _hovering ? 1.2 : 1,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.stackIcon(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
5
lib/widgets/rounded_date_picker/LICENSE
Normal file
5
lib/widgets/rounded_date_picker/LICENSE
Normal file
|
@ -0,0 +1,5 @@
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
@ -0,0 +1,332 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/flutter_rounded_button_action.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/material_rounded_date_picker_style.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/material_rounded_year_picker_style.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_date_picker_header.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_day_picker.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_month_picker.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_year_picker.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
///
|
||||
/// This file uses code taken from https://github.com/benznest/flutter_rounded_date_picker
|
||||
///
|
||||
|
||||
class FlutterRoundedDatePickerDialog extends StatefulWidget {
|
||||
const FlutterRoundedDatePickerDialog(
|
||||
{Key? key,
|
||||
this.height,
|
||||
required this.initialDate,
|
||||
required this.firstDate,
|
||||
required this.lastDate,
|
||||
this.selectableDayPredicate,
|
||||
required this.initialDatePickerMode,
|
||||
required this.era,
|
||||
this.locale,
|
||||
required this.borderRadius,
|
||||
this.imageHeader,
|
||||
this.description = "",
|
||||
this.fontFamily,
|
||||
this.textNegativeButton,
|
||||
this.textPositiveButton,
|
||||
this.textActionButton,
|
||||
this.onTapActionButton,
|
||||
this.styleDatePicker,
|
||||
this.styleYearPicker,
|
||||
this.customWeekDays,
|
||||
this.builderDay,
|
||||
this.listDateDisabled,
|
||||
this.onTapDay,
|
||||
this.onMonthChange})
|
||||
: super(key: key);
|
||||
|
||||
final DateTime initialDate;
|
||||
final DateTime firstDate;
|
||||
final DateTime lastDate;
|
||||
final SelectableDayPredicate? selectableDayPredicate;
|
||||
final DatePickerMode initialDatePickerMode;
|
||||
|
||||
/// double height.
|
||||
final double? height;
|
||||
|
||||
/// Custom era year.
|
||||
final EraMode era;
|
||||
final Locale? locale;
|
||||
|
||||
/// Border
|
||||
final double borderRadius;
|
||||
|
||||
/// Header;
|
||||
final ImageProvider? imageHeader;
|
||||
final String description;
|
||||
|
||||
/// Font
|
||||
final String? fontFamily;
|
||||
|
||||
/// Button
|
||||
final String? textNegativeButton;
|
||||
final String? textPositiveButton;
|
||||
final String? textActionButton;
|
||||
|
||||
final VoidCallback? onTapActionButton;
|
||||
|
||||
/// Style
|
||||
final MaterialRoundedDatePickerStyle? styleDatePicker;
|
||||
final MaterialRoundedYearPickerStyle? styleYearPicker;
|
||||
|
||||
/// Custom Weekday
|
||||
final List<String>? customWeekDays;
|
||||
|
||||
final BuilderDayOfDatePicker? builderDay;
|
||||
|
||||
final List<DateTime>? listDateDisabled;
|
||||
final OnTapDay? onTapDay;
|
||||
|
||||
final Function? onMonthChange;
|
||||
|
||||
@override
|
||||
_FlutterRoundedDatePickerDialogState createState() =>
|
||||
_FlutterRoundedDatePickerDialogState();
|
||||
}
|
||||
|
||||
class _FlutterRoundedDatePickerDialogState
|
||||
extends State<FlutterRoundedDatePickerDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedDate = widget.initialDate;
|
||||
_mode = widget.initialDatePickerMode;
|
||||
}
|
||||
|
||||
bool _announcedInitialDate = false;
|
||||
|
||||
late MaterialLocalizations localizations;
|
||||
late TextDirection textDirection;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
localizations = MaterialLocalizations.of(context);
|
||||
textDirection = Directionality.of(context);
|
||||
if (!_announcedInitialDate) {
|
||||
_announcedInitialDate = true;
|
||||
SemanticsService.announce(
|
||||
localizations.formatFullDate(_selectedDate),
|
||||
textDirection,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
late DateTime _selectedDate;
|
||||
late DatePickerMode _mode;
|
||||
final GlobalKey _pickerKey = GlobalKey();
|
||||
|
||||
void _vibrate() {
|
||||
switch (Theme.of(context).platform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
HapticFeedback.vibrate();
|
||||
break;
|
||||
case TargetPlatform.iOS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleModeChanged(DatePickerMode mode) {
|
||||
_vibrate();
|
||||
setState(() {
|
||||
_mode = mode;
|
||||
if (_mode == DatePickerMode.day) {
|
||||
SemanticsService.announce(
|
||||
localizations.formatMonthYear(_selectedDate),
|
||||
textDirection,
|
||||
);
|
||||
} else {
|
||||
SemanticsService.announce(
|
||||
localizations.formatYear(_selectedDate),
|
||||
textDirection,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handleYearChanged(DateTime value) async {
|
||||
if (value.isBefore(widget.firstDate)) {
|
||||
value = widget.firstDate;
|
||||
} else if (value.isAfter(widget.lastDate)) {
|
||||
value = widget.lastDate;
|
||||
}
|
||||
if (value == _selectedDate) return;
|
||||
|
||||
if (widget.onMonthChange != null) await widget.onMonthChange!(value);
|
||||
|
||||
_vibrate();
|
||||
setState(() {
|
||||
_mode = DatePickerMode.day;
|
||||
_selectedDate = value;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleDayChanged(DateTime value) {
|
||||
_vibrate();
|
||||
setState(() {
|
||||
_selectedDate = value;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleCancel() {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
void _handleOk() {
|
||||
Navigator.of(context).pop(_selectedDate);
|
||||
}
|
||||
|
||||
Widget _buildPicker() {
|
||||
switch (_mode) {
|
||||
case DatePickerMode.year:
|
||||
return FlutterRoundedYearPicker(
|
||||
key: _pickerKey,
|
||||
selectedDate: _selectedDate,
|
||||
onChanged: (DateTime date) async => await _handleYearChanged(date),
|
||||
firstDate: widget.firstDate,
|
||||
lastDate: widget.lastDate,
|
||||
era: widget.era,
|
||||
fontFamily: widget.fontFamily,
|
||||
style: widget.styleYearPicker,
|
||||
);
|
||||
case DatePickerMode.day:
|
||||
default:
|
||||
return FlutterRoundedMonthPicker(
|
||||
key: _pickerKey,
|
||||
selectedDate: _selectedDate,
|
||||
onChanged: _handleDayChanged,
|
||||
firstDate: widget.firstDate,
|
||||
lastDate: widget.lastDate,
|
||||
era: widget.era,
|
||||
locale: widget.locale,
|
||||
selectableDayPredicate: widget.selectableDayPredicate,
|
||||
fontFamily: widget.fontFamily,
|
||||
style: widget.styleDatePicker,
|
||||
borderRadius: widget.borderRadius,
|
||||
customWeekDays: widget.customWeekDays,
|
||||
builderDay: widget.builderDay,
|
||||
listDateDisabled: widget.listDateDisabled,
|
||||
onTapDay: widget.onTapDay,
|
||||
onMonthChange: widget.onMonthChange);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final Widget picker = _buildPicker();
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
final Widget actions = FlutterRoundedButtonAction(
|
||||
textButtonNegative: widget.textNegativeButton,
|
||||
textButtonPositive: widget.textPositiveButton,
|
||||
onTapButtonNegative: _handleCancel,
|
||||
onTapButtonPositive: _handleOk,
|
||||
textActionButton: widget.textActionButton,
|
||||
onTapButtonAction: widget.onTapActionButton,
|
||||
localizations: localizations,
|
||||
textStyleButtonNegative: widget.styleDatePicker?.textStyleButtonNegative,
|
||||
textStyleButtonPositive: widget.styleDatePicker?.textStyleButtonPositive,
|
||||
textStyleButtonAction: widget.styleDatePicker?.textStyleButtonAction,
|
||||
borderRadius: widget.borderRadius,
|
||||
paddingActionBar: widget.styleDatePicker?.paddingActionBar,
|
||||
background: widget.styleDatePicker?.backgroundActionBar,
|
||||
);
|
||||
|
||||
Color backgroundPicker = theme.dialogBackgroundColor;
|
||||
if (_mode == DatePickerMode.day) {
|
||||
backgroundPicker = widget.styleDatePicker?.backgroundPicker ??
|
||||
theme.dialogBackgroundColor;
|
||||
} else {
|
||||
backgroundPicker = widget.styleYearPicker?.backgroundPicker ??
|
||||
theme.dialogBackgroundColor;
|
||||
}
|
||||
|
||||
final Dialog dialog = Dialog(
|
||||
child: OrientationBuilder(
|
||||
builder: (BuildContext context, Orientation orientation) {
|
||||
final Widget header = FlutterRoundedDatePickerHeader(
|
||||
selectedDate: _selectedDate,
|
||||
mode: _mode,
|
||||
onModeChanged: _handleModeChanged,
|
||||
orientation: orientation,
|
||||
era: widget.era,
|
||||
borderRadius: widget.borderRadius,
|
||||
imageHeader: widget.imageHeader,
|
||||
description: widget.description,
|
||||
fontFamily: widget.fontFamily,
|
||||
style: widget.styleDatePicker);
|
||||
switch (orientation) {
|
||||
case Orientation.landscape:
|
||||
return Container(
|
||||
height: isDesktop ? 600 : null,
|
||||
width: isDesktop ? 700 : null,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundPicker,
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Flexible(flex: 1, child: header),
|
||||
Flexible(
|
||||
flex: 2, // have the picker take up 2/3 of the dialog width
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: isDesktop ? 530 : null,
|
||||
width: isDesktop ? 700 : null,
|
||||
child: picker),
|
||||
actions,
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
case Orientation.portrait:
|
||||
default:
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundPicker,
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
header,
|
||||
if (widget.height == null)
|
||||
Flexible(child: picker)
|
||||
else
|
||||
SizedBox(
|
||||
height: widget.height,
|
||||
child: picker,
|
||||
),
|
||||
actions,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return Theme(
|
||||
data: theme.copyWith(dialogBackgroundColor: Colors.transparent),
|
||||
child: dialog,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
// import 'package:flutter_rounded_date_picker/src/dialogs/flutter_rounded_date_picker_dialog.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/era_mode.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/material_rounded_date_picker_style.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/material_rounded_year_picker_style.dart';
|
||||
import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_day_picker.dart';
|
||||
import 'package:stackwallet/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart';
|
||||
|
||||
///
|
||||
/// This file uses code taken from https://github.com/benznest/flutter_rounded_date_picker
|
||||
///
|
||||
|
||||
// Examples can assume:
|
||||
// BuildContext context;
|
||||
|
||||
/// Initial display mode of the date picker dialog.
|
||||
///
|
||||
/// Date picker UI mode for either showing a list of available years or a
|
||||
/// monthly calendar initially in the dialog shown by calling [showDatePicker].
|
||||
///
|
||||
|
||||
// Shows the selected date in large font and toggles between year and day mode
|
||||
|
||||
/// Signature for predicating dates for enabled date selections.
|
||||
///
|
||||
/// See [showDatePicker].
|
||||
typedef SelectableDayPredicate = bool Function(DateTime day);
|
||||
|
||||
/// Shows a dialog containing a material design date picker.
|
||||
///
|
||||
/// The returned [Future] resolves to the date selected by the user when the
|
||||
/// user closes the dialog. If the user cancels the dialog, null is returned.
|
||||
///
|
||||
/// An optional [selectableDayPredicate] function can be passed in to customize
|
||||
/// the days to enable for selection. If provided, only the days that
|
||||
/// [selectableDayPredicate] returned true for will be selectable.
|
||||
///
|
||||
/// An optional [initialDatePickerMode] argument can be used to display the
|
||||
/// date picker initially in the year or month+day picker mode. It defaults
|
||||
/// to month+day, and must not be null.
|
||||
///
|
||||
/// An optional [locale] argument can be used to set the locale for the date
|
||||
/// picker. It defaults to the ambient locale provided by [Localizations].
|
||||
///
|
||||
/// An optional [textDirection] argument can be used to set the text direction
|
||||
/// (RTL or LTR) for the date picker. It defaults to the ambient text direction
|
||||
/// provided by [Directionality]. If both [locale] and [textDirection] are not
|
||||
/// null, [textDirection] overrides the direction chosen for the [locale].
|
||||
///
|
||||
/// The [context] argument is passed to [showDialog], the documentation for
|
||||
/// which discusses how it is used.
|
||||
///
|
||||
/// The [builder] parameter can be used to wrap the dialog widget
|
||||
/// to add inherited widgets like [Theme].
|
||||
///
|
||||
/// {@tool sample}
|
||||
/// Show a date picker with the dark theme.
|
||||
///
|
||||
/// ```dart
|
||||
/// Future<DateTime> selectedDate = showDatePicker(
|
||||
/// context: context,
|
||||
/// initialDate: DateTime.now(),
|
||||
/// firstDate: DateTime(2018),
|
||||
/// lastDate: DateTime(2030),
|
||||
/// builder: (BuildContext context, Widget child) {
|
||||
/// return Theme(
|
||||
/// data: ThemeData.dark(),
|
||||
/// child: child,
|
||||
/// );
|
||||
/// },
|
||||
/// );
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// The [context], [initialDate], [firstDate], and [lastDate] parameters must
|
||||
/// not be null.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [showTimePicker], which shows a dialog that contains a material design
|
||||
/// time picker.
|
||||
/// * [DayPicker], which displays the days of a given month and allows
|
||||
/// choosing a day.
|
||||
/// * [MonthPicker], which displays a scrollable list of months to allow
|
||||
/// picking a month.
|
||||
/// * [YearPicker], which displays a scrollable list of years to allow picking
|
||||
/// a year.
|
||||
///
|
||||
|
||||
Future<DateTime?> showRoundedDatePicker(
|
||||
{required BuildContext context,
|
||||
double? height,
|
||||
DateTime? initialDate,
|
||||
DateTime? firstDate,
|
||||
DateTime? lastDate,
|
||||
SelectableDayPredicate? selectableDayPredicate,
|
||||
DatePickerMode initialDatePickerMode = DatePickerMode.day,
|
||||
Locale? locale,
|
||||
TextDirection? textDirection,
|
||||
ThemeData? theme,
|
||||
double borderRadius = 16,
|
||||
EraMode era = EraMode.CHRIST_YEAR,
|
||||
ImageProvider? imageHeader,
|
||||
String description = "",
|
||||
String? fontFamily,
|
||||
bool barrierDismissible = false,
|
||||
Color background = Colors.transparent,
|
||||
String? textNegativeButton,
|
||||
String? textPositiveButton,
|
||||
String? textActionButton,
|
||||
VoidCallback? onTapActionButton,
|
||||
MaterialRoundedDatePickerStyle? styleDatePicker,
|
||||
MaterialRoundedYearPickerStyle? styleYearPicker,
|
||||
List<String>? customWeekDays,
|
||||
BuilderDayOfDatePicker? builderDay,
|
||||
List<DateTime>? listDateDisabled,
|
||||
OnTapDay? onTapDay,
|
||||
Function? onMonthChange}) async {
|
||||
initialDate ??= DateTime.now();
|
||||
firstDate ??= DateTime(initialDate.year - 1);
|
||||
lastDate ??= DateTime(initialDate.year + 1);
|
||||
theme ??= ThemeData();
|
||||
|
||||
assert(
|
||||
!initialDate.isBefore(firstDate),
|
||||
'initialDate must be on or after firstDate',
|
||||
);
|
||||
assert(
|
||||
!initialDate.isAfter(lastDate),
|
||||
'initialDate must be on or before lastDate',
|
||||
);
|
||||
assert(
|
||||
!firstDate.isAfter(lastDate),
|
||||
'lastDate must be on or after firstDate',
|
||||
);
|
||||
assert(
|
||||
selectableDayPredicate == null || selectableDayPredicate(initialDate),
|
||||
'Provided initialDate must satisfy provided selectableDayPredicate',
|
||||
);
|
||||
assert(
|
||||
(onTapActionButton != null && textActionButton != null) ||
|
||||
onTapActionButton == null,
|
||||
"If you provide onLeftBtn, you must provide leftBtn",
|
||||
);
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
Widget child = GestureDetector(
|
||||
onTap: () {
|
||||
if (!barrierDismissible) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: background,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
//
|
||||
},
|
||||
child: FlutterRoundedDatePickerDialog(
|
||||
height: height,
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
selectableDayPredicate: selectableDayPredicate,
|
||||
initialDatePickerMode: initialDatePickerMode,
|
||||
era: era,
|
||||
locale: locale,
|
||||
borderRadius: borderRadius,
|
||||
imageHeader: imageHeader,
|
||||
description: description,
|
||||
fontFamily: fontFamily,
|
||||
textNegativeButton: textNegativeButton,
|
||||
textPositiveButton: textPositiveButton,
|
||||
textActionButton: textActionButton,
|
||||
onTapActionButton: onTapActionButton,
|
||||
styleDatePicker: styleDatePicker,
|
||||
styleYearPicker: styleYearPicker,
|
||||
customWeekDays: customWeekDays,
|
||||
builderDay: builderDay,
|
||||
listDateDisabled: listDateDisabled,
|
||||
onTapDay: onTapDay,
|
||||
onMonthChange: onMonthChange,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (textDirection != null) {
|
||||
child = Directionality(
|
||||
textDirection: textDirection,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
if (locale != null) {
|
||||
child = Localizations.override(
|
||||
context: context,
|
||||
locale: locale,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
return await showDialog<DateTime>(
|
||||
context: context,
|
||||
barrierDismissible: barrierDismissible,
|
||||
builder: (_) => Theme(data: theme!, child: child),
|
||||
);
|
||||
}
|
|
@ -3,7 +3,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|||
import 'package:stackwallet/widgets/expandable.dart';
|
||||
import 'package:stackwallet/widgets/table_view/table_view_cell.dart';
|
||||
|
||||
class TableViewRow extends StatelessWidget {
|
||||
class TableViewRow extends StatefulWidget {
|
||||
const TableViewRow({
|
||||
Key? key,
|
||||
required this.cells,
|
||||
|
@ -17,40 +17,66 @@ class TableViewRow extends StatelessWidget {
|
|||
|
||||
final List<TableViewCell> cells;
|
||||
final Widget? expandingChild;
|
||||
final Decoration? decoration;
|
||||
final BoxDecoration? decoration;
|
||||
final void Function(ExpandableState)? onExpandChanged;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final double spacing;
|
||||
final CrossAxisAlignment crossAxisAlignment;
|
||||
|
||||
@override
|
||||
State<TableViewRow> createState() => _TableViewRowState();
|
||||
}
|
||||
|
||||
class _TableViewRowState extends State<TableViewRow> {
|
||||
late final List<TableViewCell> cells;
|
||||
late final Widget? expandingChild;
|
||||
late final BoxDecoration? decoration;
|
||||
late final void Function(ExpandableState)? onExpandChanged;
|
||||
late final EdgeInsetsGeometry padding;
|
||||
late final double spacing;
|
||||
late final CrossAxisAlignment crossAxisAlignment;
|
||||
|
||||
bool _hovering = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
cells = widget.cells;
|
||||
expandingChild = widget.expandingChild;
|
||||
decoration = widget.decoration;
|
||||
onExpandChanged = widget.onExpandChanged;
|
||||
padding = widget.padding;
|
||||
spacing = widget.spacing;
|
||||
crossAxisAlignment = widget.crossAxisAlignment;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: decoration,
|
||||
decoration: !_hovering
|
||||
? decoration
|
||||
: decoration?.copyWith(
|
||||
boxShadow: [
|
||||
Theme.of(context).extension<StackColors>()!.standardBoxShadow,
|
||||
Theme.of(context).extension<StackColors>()!.standardBoxShadow,
|
||||
],
|
||||
),
|
||||
child: expandingChild == null
|
||||
? Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
children: [
|
||||
for (int i = 0; i < cells.length; i++) ...[
|
||||
if (i != 0 && i != cells.length)
|
||||
SizedBox(
|
||||
width: spacing,
|
||||
),
|
||||
Expanded(
|
||||
flex: cells[i].flex,
|
||||
child: cells[i],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expandable(
|
||||
onExpandChanged: onExpandChanged,
|
||||
header: Padding(
|
||||
? MouseRegion(
|
||||
onEnter: (_) {
|
||||
setState(() {
|
||||
_hovering = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
setState(() {
|
||||
_hovering = false;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
children: [
|
||||
for (int i = 0; i < cells.length; i++) ...[
|
||||
if (i != 0 && i != cells.length)
|
||||
|
@ -65,6 +91,38 @@ class TableViewRow extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Expandable(
|
||||
onExpandChanged: onExpandChanged,
|
||||
header: MouseRegion(
|
||||
onEnter: (_) {
|
||||
setState(() {
|
||||
_hovering = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
setState(() {
|
||||
_hovering = false;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
children: [
|
||||
for (int i = 0; i < cells.length; i++) ...[
|
||||
if (i != 0 && i != cells.length)
|
||||
SizedBox(
|
||||
width: spacing,
|
||||
),
|
||||
Expanded(
|
||||
flex: cells[i].flex,
|
||||
child: cells[i],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Container(
|
||||
|
|
|
@ -14,10 +14,12 @@ class WalletInfoRow extends ConsumerWidget {
|
|||
Key? key,
|
||||
required this.walletId,
|
||||
this.onPressed,
|
||||
this.padding = const EdgeInsets.all(0),
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final VoidCallback? onPressed;
|
||||
final EdgeInsets padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
@ -30,53 +32,56 @@ class WalletInfoRow extends ConsumerWidget {
|
|||
cursor: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(coin: manager.coin),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
manager.walletName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
child: Padding(
|
||||
padding: padding,
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(coin: manager.coin),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
manager.walletName,
|
||||
style: STextStyles.desktopTextExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: WalletInfoRowBalanceFuture(
|
||||
walletId: walletId,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: WalletInfoRowBalanceFuture(
|
||||
walletId: walletId,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
.textSubtitle1,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue