various frost settings view ui tweaks and fixes

This commit is contained in:
julian 2024-04-29 15:35:15 -06:00
parent 3bddec00f1
commit a9449ace0d
4 changed files with 456 additions and 42 deletions

View file

@ -1,8 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart'; import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart'; import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
@ -11,6 +14,7 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.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_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class FrostParticipantsView extends ConsumerWidget { class FrostParticipantsView extends ConsumerWidget {
const FrostParticipantsView({ const FrostParticipantsView({
@ -84,31 +88,56 @@ class FrostParticipantsView extends ConsumerWidget {
), ),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: Util.isDesktop
? CrossAxisAlignment.start
: CrossAxisAlignment.stretch,
children: [ children: [
for (int i = 0; i < frostInfo.participants.length; i++) for (int i = 0; i < frostInfo.participants.length; i++)
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 8, vertical: 5,
), ),
child: Column( child: RoundedWhiteContainer(
mainAxisSize: MainAxisSize.min, child: Row(
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Container(
Text( width: 26,
"Index $i", height: 26,
style: STextStyles.label(context), decoration: BoxDecoration(
), color: Theme.of(context)
const SizedBox( .extension<StackColors>()!
height: 6, .textFieldActiveBG,
), borderRadius: BorderRadius.circular(
SelectableText( 200,
frostInfo.participants[i] == frostInfo.myName ),
? "${frostInfo.participants[i]} (me)" ),
: frostInfo.participants[i], child: Center(
style: STextStyles.itemSubtitle12(context), child: SvgPicture.asset(
), Assets.svg.user,
], width: 16,
height: 16,
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: Text(
frostInfo.participants[i] == frostInfo.myName
? "${frostInfo.participants[i]} (me)"
: frostInfo.participants[i],
style: STextStyles.w500_14(context),
),
),
const SizedBox(
width: 8,
),
IconCopyButton(
data: frostInfo.participants[i],
),
],
),
), ),
), ),
], ],

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -19,6 +21,7 @@ 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_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
final class CompleteReshareConfigView extends ConsumerStatefulWidget { final class CompleteReshareConfigView extends ConsumerStatefulWidget {
@ -45,9 +48,12 @@ class _CompleteReshareConfigViewState
final List<TextEditingController> controllers = []; final List<TextEditingController> controllers = [];
late final String myName;
int _participantsCount = 0; int _participantsCount = 0;
bool _buttonLock = false; bool _buttonLock = false;
bool _includeMeInReshare = false;
Future<void> _onPressed() async { Future<void> _onPressed() async {
if (_buttonLock) { if (_buttonLock) {
@ -74,10 +80,16 @@ class _CompleteReshareConfigViewState
); );
} }
final List<String> newParticipants =
controllers.map((e) => e.text.trim()).toList();
if (_includeMeInReshare) {
newParticipants.insert(0, myName);
}
final config = Frost.createResharerConfig( final config = Frost.createResharerConfig(
newThreshold: int.parse(_newThresholdController.text), newThreshold: int.parse(_newThresholdController.text),
resharers: widget.resharers, resharers: widget.resharers,
newParticipants: controllers.map((e) => e.text).toList(), newParticipants: newParticipants,
); );
final salt = Format.uint8listToString( final salt = Format.uint8listToString(
@ -105,7 +117,7 @@ class _CompleteReshareConfigViewState
}); });
} }
ref.read(pFrostResharingData).myName = frostInfo.myName; ref.read(pFrostResharingData).myName = myName;
ref.read(pFrostResharingData).resharerConfig = config; ref.read(pFrostResharingData).resharerConfig = config;
if (mounted) { if (mounted) {
@ -152,18 +164,29 @@ class _CompleteReshareConfigViewState
return "At least two participants required"; return "At least two participants required";
} }
if (controllers.length != partsCount) { final newParticipants = controllers.map((e) => e.text.trim()).toList();
if (newParticipants.contains(myName)) {
return "Using your own name should be done using the checkbox to include"
" yourself";
}
if (_includeMeInReshare) {
newParticipants.add(myName);
}
if (newParticipants.length != partsCount) {
return "Participants count error"; return "Participants count error";
} }
final hasEmptyParticipants = controllers final hasEmptyParticipants = newParticipants
.map((e) => e.text.isEmpty) .map((e) => e.trim().isEmpty)
.reduce((value, element) => value |= element); .reduce((value, element) => value |= element);
if (hasEmptyParticipants) { if (hasEmptyParticipants) {
return "Participants must not be empty"; return "Participants must not be empty";
} }
if (controllers.length != controllers.map((e) => e.text).toSet().length) { if (newParticipants.length != newParticipants.toSet().length) {
return "Duplicate participant name found"; return "Duplicate participant name found";
} }
@ -171,8 +194,12 @@ class _CompleteReshareConfigViewState
} }
void _participantsCountChanged(String newValue) { void _participantsCountChanged(String newValue) {
final count = int.tryParse(newValue); int? count = int.tryParse(newValue);
if (count != null) { if (count != null) {
if (_includeMeInReshare) {
count = max(0, count - 1);
}
if (count > _participantsCount) { if (count > _participantsCount) {
for (int i = _participantsCount; i < count; i++) { for (int i = _participantsCount; i < count; i++) {
controllers.add(TextEditingController()); controllers.add(TextEditingController());
@ -192,6 +219,17 @@ class _CompleteReshareConfigViewState
} }
} }
@override
void initState() {
final frostInfo = ref
.read(mainDBProvider)
.isar
.frostWalletInfo
.getByWalletIdSync(widget.walletId)!;
myName = frostInfo.myName;
super.initState();
}
@override @override
void dispose() { void dispose() {
_newThresholdController.dispose(); _newThresholdController.dispose();
@ -231,7 +269,7 @@ class _CompleteReshareConfigViewState
}, },
), ),
title: Text( title: Text(
"Modify Participants", "Edit group details",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),
@ -258,11 +296,62 @@ class _CompleteReshareConfigViewState
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: Util.isDesktop
? CrossAxisAlignment.start
: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(
height: 8,
),
GestureDetector(
onTap: () {
setState(() {
_includeMeInReshare = !_includeMeInReshare;
});
_participantsCountChanged(_newParticipantsCountController.text);
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _includeMeInReshare,
onChanged: (value) {
setState(
() => _includeMeInReshare = value == true,
);
_participantsCountChanged(
_newParticipantsCountController.text);
},
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I will be a signer in the new config",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
const SizedBox(
height: 20,
),
Text( Text(
"New threshold", "New threshold",
style: STextStyles.label(context), style: STextStyles.w500_14(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
@ -271,13 +360,32 @@ class _CompleteReshareConfigViewState
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
controller: _newThresholdController, controller: _newThresholdController,
decoration: InputDecoration(
hintText: "Enter number of signatures",
hintStyle: STextStyles.fieldLabel(context),
),
),
const SizedBox(
height: 6,
),
RoundedWhiteContainer(
child: Text(
"Enter number of signatures required for fund management.",
style: STextStyles.w500_12(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle2,
),
),
), ),
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
Text( Text(
"Number of participants", "New number of participants",
style: STextStyles.label(context), style: STextStyles.w500_14(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
@ -287,6 +395,23 @@ class _CompleteReshareConfigViewState
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
controller: _newParticipantsCountController, controller: _newParticipantsCountController,
onChanged: _participantsCountChanged, onChanged: _participantsCountChanged,
decoration: InputDecoration(
hintText: "Enter number of participants",
hintStyle: STextStyles.fieldLabel(context),
),
),
const SizedBox(
height: 6,
),
RoundedWhiteContainer(
child: Text(
"The number of participants must be equal to or less than the"
" number of required signatures.",
style: STextStyles.w500_12(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle2,
),
),
), ),
const SizedBox( const SizedBox(
height: 16, height: 16,
@ -294,12 +419,26 @@ class _CompleteReshareConfigViewState
if (controllers.isNotEmpty) if (controllers.isNotEmpty)
Text( Text(
"Participants", "Participants",
style: STextStyles.label(context), style: STextStyles.w500_14(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
), ),
if (controllers.isNotEmpty) if (controllers.isNotEmpty)
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
if (controllers.isNotEmpty)
RoundedWhiteContainer(
child: Text(
"Type each name in one word without spaces.",
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
),
),
),
if (controllers.isNotEmpty) if (controllers.isNotEmpty)
Column( Column(
children: [ children: [
@ -310,6 +449,10 @@ class _CompleteReshareConfigViewState
), ),
child: TextField( child: TextField(
controller: controllers[i], controller: controllers[i],
decoration: InputDecoration(
hintText: "Enter name",
hintStyle: STextStyles.fieldLabel(context),
),
), ),
), ),
], ],

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/begin_resharing_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/involved/step_2/begin_resharing_view.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
@ -8,6 +9,7 @@ import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/frost.dart'; import 'package:stackwallet/services/frost.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
@ -20,7 +22,10 @@ import 'package:stackwallet/widgets/custom_buttons/simple_copy_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/detail_item.dart'; import 'package:stackwallet/widgets/detail_item.dart';
import 'package:stackwallet/widgets/dialogs/simple_mobile_dialog.dart';
import 'package:stackwallet/widgets/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
class DisplayReshareConfigView extends ConsumerStatefulWidget { class DisplayReshareConfigView extends ConsumerStatefulWidget {
@ -40,9 +45,19 @@ class DisplayReshareConfigView extends ConsumerStatefulWidget {
class _DisplayReshareConfigViewState class _DisplayReshareConfigViewState
extends ConsumerState<DisplayReshareConfigView> { extends ConsumerState<DisplayReshareConfigView> {
static const info = [
"Share this config with the signing group participants as well as any new "
"participant.",
"Wait for them to import the config.",
"Verify that everyone has imported the config. If you try to continue "
"before everyone is ready, the process will be canceled.",
"Check the box and press “Start resharing”.",
];
late final bool iAmInvolved; late final bool iAmInvolved;
bool _buttonLock = false; bool _buttonLock = false;
bool _userVerifyContinue = false;
Future<void> _onPressed() async { Future<void> _onPressed() async {
if (_buttonLock) { if (_buttonLock) {
@ -88,6 +103,111 @@ class _DisplayReshareConfigViewState
} }
} }
void _showParticipantsDialog() {
final participants =
ref.read(pFrostResharingData).configData!.newParticipants;
showDialog<void>(
context: context,
builder: (_) => SimpleMobileDialog(
showCloseButton: false,
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 24,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
"Group participants",
style: STextStyles.w600_20(context),
),
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
"The names are case-sensitive and must be entered exactly.",
style: STextStyles.w400_16(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
),
),
const SizedBox(
height: 12,
),
for (final participant in participants)
Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
height: 1.5,
color:
Theme.of(context).extension<StackColors>()!.background,
),
const SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
children: [
Container(
width: 26,
height: 26,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
borderRadius: BorderRadius.circular(
200,
),
),
child: Center(
child: SvgPicture.asset(
Assets.svg.user,
width: 16,
height: 16,
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: Text(
participant,
style: STextStyles.w500_14(context),
),
),
const SizedBox(
width: 8,
),
IconCopyButton(
data: participant,
),
],
),
),
const SizedBox(
height: 12,
),
],
),
const SizedBox(
height: 24,
),
],
),
),
);
}
@override @override
void initState() { void initState() {
// TODO: optimize this by creating watcher providers (similar to normal WalletInfo) // TODO: optimize this by creating watcher providers (similar to normal WalletInfo)
@ -162,7 +282,13 @@ class _DisplayReshareConfigViewState
), ),
child: Column( child: Column(
children: [ children: [
if (!Util.isDesktop) const Spacer(), const SizedBox(
height: 16,
),
const FrostStepUserSteps(
userSteps: info,
),
const SizedBox(height: 20),
SizedBox( SizedBox(
height: 220, height: 220,
child: Row( child: Row(
@ -197,13 +323,66 @@ class _DisplayReshareConfigViewState
SizedBox( SizedBox(
height: Util.isDesktop ? 64 : 16, height: Util.isDesktop ? 64 : 16,
), ),
if (!Util.isDesktop) Row(
const Spacer( children: [
flex: 2, Expanded(
child: SecondaryButton(
label: "Show group participants",
onPressed: _showParticipantsDialog,
),
),
],
),
if (iAmInvolved && !Util.isDesktop) const Spacer(),
if (iAmInvolved)
const SizedBox(
height: 16,
),
if (iAmInvolved)
GestureDetector(
onTap: () {
setState(() {
_userVerifyContinue = !_userVerifyContinue;
});
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I have verified that everyone has imported the config",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
if (iAmInvolved)
const SizedBox(
height: 16,
), ),
if (iAmInvolved) if (iAmInvolved)
PrimaryButton( PrimaryButton(
label: "Start resharing", label: "Start resharing",
enabled: _userVerifyContinue,
onPressed: _onPressed, onPressed: _onPressed,
), ),
], ],

View file

@ -21,6 +21,7 @@ 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_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/frost_step_user_steps.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
@ -45,12 +46,22 @@ class ImportReshareConfigView extends ConsumerStatefulWidget {
class _ImportReshareConfigViewState class _ImportReshareConfigViewState
extends ConsumerState<ImportReshareConfigView> { extends ConsumerState<ImportReshareConfigView> {
static const info = [
"Scan the config QR code or paste the code provided by the group member who"
" is initiating resharing.",
"Wait for other participants to finish importing the config.",
"Verify that everyone has filled out their forms before continuing. If you "
"try to continue before everyone is ready, the process will be canceled.",
"Check the box and press “Start resharing”.",
];
late final TextEditingController configFieldController; late final TextEditingController configFieldController;
late final FocusNode configFocusNode; late final FocusNode configFocusNode;
bool _configEmpty = true; bool _configEmpty = true;
bool _buttonLock = false; bool _buttonLock = false;
bool _userVerifyContinue = false;
Future<void> _onPressed() async { Future<void> _onPressed() async {
if (_buttonLock) { if (_buttonLock) {
@ -84,14 +95,14 @@ class _ImportReshareConfigViewState
if (frostInfo.knownSalts.contains(salt)) { if (frostInfo.knownSalts.contains(salt)) {
throw Exception("Duplicate config salt"); throw Exception("Duplicate config salt");
} else { } else {
final salts = frostInfo.knownSalts; final salts = frostInfo.knownSalts.toList();
salts.add(salt); salts.add(salt);
final mainDB = ref.read(mainDBProvider); final mainDB = ref.read(mainDBProvider);
await mainDB.isar.writeTxn(() async { await mainDB.isar.writeTxn(() async {
final info = frostInfo; final id = frostInfo.id;
await mainDB.isar.frostWalletInfo.delete(info.id); await mainDB.isar.frostWalletInfo.delete(id);
await mainDB.isar.frostWalletInfo.put( await mainDB.isar.frostWalletInfo.put(
info.copyWith(knownSalts: salts), frostInfo.copyWith(knownSalts: salts),
); );
}); });
} }
@ -201,6 +212,20 @@ class _ImportReshareConfigViewState
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
const FrostStepUserSteps(
userSteps: info,
),
const SizedBox(height: 20),
Text(
"Enter config",
style: STextStyles.w500_14(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
),
const SizedBox(
height: 10,
),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius, Constants.size.circularBorderRadius,
@ -319,9 +344,47 @@ class _ImportReshareConfigViewState
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
GestureDetector(
onTap: () {
setState(() {
_userVerifyContinue = !_userVerifyContinue;
});
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 26,
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: _userVerifyContinue,
onChanged: (value) => setState(
() => _userVerifyContinue = value == true,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
"I have verified that everyone has imported the config",
style: STextStyles.w500_14(context),
),
),
],
),
),
),
const SizedBox(
height: 16,
),
PrimaryButton( PrimaryButton(
label: "Start resharing", label: "Start resharing",
enabled: !_configEmpty, enabled: !_configEmpty && _userVerifyContinue,
onPressed: () async { onPressed: () async {
if (FocusScope.of(context).hasFocus) { if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();