mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-24 06:48:38 +02:00
ui: better QR switcher (#2477)
This commit is contained in:
@@ -12,7 +12,7 @@ class QrImage extends StatelessWidget {
|
||||
this.embeddedImagePath,
|
||||
});
|
||||
|
||||
final double size;
|
||||
final double? size;
|
||||
final Color foregroundColor;
|
||||
final Color backgroundColor;
|
||||
final String data;
|
||||
|
||||
@@ -6,26 +6,23 @@ import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
import 'package:cake_wallet/src/screens/ur/widgets/urqr.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/animated_ur_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
// ur:xmr-txunsigned - unsigned transaction
|
||||
// should show a scanner afterwards.
|
||||
|
||||
class AnimatedURPage extends BasePage {
|
||||
final bool isAll;
|
||||
AnimatedURPage(this.animatedURmodel, {
|
||||
AnimatedURPage(
|
||||
this.animatedURmodel, {
|
||||
required Map<String, String> this.urQr,
|
||||
this.isAll = false,
|
||||
}) {
|
||||
if (urQr.keys.first.startsWith("export-outputs") && animatedURmodel.wallet.type == WalletType.monero) {
|
||||
if (urQr.keys.first.startsWith("export-outputs") &&
|
||||
animatedURmodel.wallet.type == WalletType.monero) {
|
||||
if (animatedURmodel.wallet.type == WalletType.monero) {
|
||||
urQr = monero!.exportOutputsUR(animatedURmodel.wallet);
|
||||
} else {
|
||||
@@ -43,7 +40,6 @@ class AnimatedURPage extends BasePage {
|
||||
|
||||
Map<String, String> urQr = {};
|
||||
|
||||
|
||||
final AnimatedURModel animatedURmodel;
|
||||
|
||||
String get urQrType {
|
||||
@@ -61,20 +57,22 @@ class AnimatedURPage extends BasePage {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 64.0),
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: URQR(
|
||||
urqr: urQr,
|
||||
walletType: animatedURmodel.wallet.type,
|
||||
),
|
||||
),
|
||||
if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header].contains(urQrType)) ...{
|
||||
SizedBox(height: 32),
|
||||
if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header]
|
||||
.contains(urQrType)) ...{
|
||||
Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: PrimaryButton(
|
||||
onPressed: () => _continue(context),
|
||||
text: "Continue",
|
||||
text: "Scan QR Code",
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
@@ -87,140 +85,44 @@ class AnimatedURPage extends BasePage {
|
||||
|
||||
Future<void> _continue(BuildContext context) async {
|
||||
try {
|
||||
switch (urQrType) {
|
||||
case "ur:xmr-txunsigned": // ur:xmr-txsigned
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
final result = await monero!.commitTransactionUR(animatedURmodel.wallet, ur);
|
||||
if (result) {
|
||||
switch (urQrType) {
|
||||
case "ur:xmr-txunsigned": // ur:xmr-txsigned
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
final result =
|
||||
await monero!.commitTransactionUR(animatedURmodel.wallet, ur);
|
||||
if (result) {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
break;
|
||||
case "ur:xmr-output": // xmr-keyimage
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
final result =
|
||||
await monero!.importKeyImagesUR(animatedURmodel.wallet, ur);
|
||||
if (result) {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
break;
|
||||
case "ur:psbt": // psbt
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
await bitcoin!
|
||||
.commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n"));
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
break;
|
||||
case "ur:xmr-output": // xmr-keyimage
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
final result = await monero!.importKeyImagesUR(animatedURmodel.wallet, ur);
|
||||
if (result) {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
break;
|
||||
case "ur:psbt": // psbt
|
||||
final ur = await presentQRScanner(context);
|
||||
if (ur == null) return;
|
||||
await bitcoin!.commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n"));
|
||||
Navigator.of(context).pop(true);
|
||||
default:
|
||||
throw UnimplementedError("unable to handle UR: ${urQrType}");
|
||||
default:
|
||||
throw UnimplementedError("unable to handle UR: ${urQrType}");
|
||||
}
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context, true));
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.pop(context, true));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class URQR extends StatefulWidget {
|
||||
URQR({super.key, required this.urqr});
|
||||
|
||||
final Map<String, String> urqr;
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_URQRState createState() => _URQRState();
|
||||
}
|
||||
|
||||
const urFrameTime = 1000 ~/ 5;
|
||||
|
||||
class _URQRState extends State<URQR> {
|
||||
Timer? t;
|
||||
int frame = 0;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setState(() {
|
||||
t = Timer.periodic(const Duration(milliseconds: urFrameTime), (timer) {
|
||||
_nextFrame();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _nextFrame() {
|
||||
setState(() {
|
||||
frame++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
t?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
late String selected = (widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first;
|
||||
int selectedInt = 0;
|
||||
|
||||
List<String> get frames {
|
||||
return widget.urqr[selected]?.split("\n") ?? [];
|
||||
}
|
||||
|
||||
late String nextLabel = widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length] ;
|
||||
|
||||
void next() {
|
||||
final keys = widget.urqr.keys.toList();
|
||||
setState(() {
|
||||
selectedInt++;
|
||||
nextLabel = keys[(selectedInt + 1) % keys.length];
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: QrImage(
|
||||
data: frames[frame % frames.length],
|
||||
version: -1,
|
||||
size: 400,
|
||||
),
|
||||
),
|
||||
if (widget.urqr.values.length > 1)
|
||||
SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: PrimaryButton(
|
||||
onPressed: next,
|
||||
text: nextLabel,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (FeatureFlag.hasDevOptions) ...{
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: """Current frame (${frame % frames.length}): ${frames[frame % frames.length]},
|
||||
All frames:
|
||||
- ${frames.join("\n - ")}"""));
|
||||
},
|
||||
child: Text("[dev] copy debug info"),
|
||||
),
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class QRFormatInfoBottomSheet extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: SvgPicture.asset('assets/images/urqr_icon.svg'),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
Text(
|
||||
S.of(context).qr_code_format,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
S.of(context).qr_code_format_note,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
S.of(context).qr_code_format_body,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// ignore: must_be_immutable
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class QRFormatSelectionDialog extends BaseAlertDialog {
|
||||
final List<String> formats;
|
||||
final int currentIndex;
|
||||
final Function(int) onFormatSelected;
|
||||
|
||||
QRFormatSelectionDialog({
|
||||
required this.formats,
|
||||
required this.currentIndex,
|
||||
required this.onFormatSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
String get titleText => S.current.choose_qr_code_format;
|
||||
|
||||
@override
|
||||
Widget content(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
S.current.choose_qr_code_format_note,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 16,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
...formats.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final format = entry.value;
|
||||
final isSelected = index == currentIndex;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.only(bottom: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: isSelected
|
||||
? Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 2,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () => onFormatSelected(index),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
if (format.startsWith('Cupcake'))
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/cupcake.svg',
|
||||
width: 32,
|
||||
height: 32,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_getFormatName(format),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isSelected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_getFormatSubtitle(format),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isSelected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer
|
||||
.withOpacity(0.6)
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isSelected) ...[
|
||||
Icon(
|
||||
Icons.check_circle,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get leftActionButtonText => "";
|
||||
|
||||
@override
|
||||
String get rightActionButtonText => "";
|
||||
|
||||
@override
|
||||
VoidCallback get actionLeft => () {};
|
||||
|
||||
@override
|
||||
VoidCallback get actionRight => () {};
|
||||
|
||||
@override
|
||||
bool get isBottomDividerExists => false;
|
||||
|
||||
@override
|
||||
Widget actionButtons(BuildContext context) {
|
||||
return SizedBox.shrink(); // get rid fo cancel button
|
||||
}
|
||||
|
||||
String _getFormatName(String format) {
|
||||
return format.split(' ')[0];
|
||||
}
|
||||
|
||||
String _getFormatSubtitle(String format) {
|
||||
return format.substring(format.indexOf(' ') + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
import 'package:cake_wallet/src/screens/ur/widgets/qr_format_info_bottom_sheet.dart';
|
||||
import 'package:cake_wallet/src/screens/ur/widgets/qr_selection_dialog.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class URQR extends StatefulWidget {
|
||||
URQR({super.key, required this.urqr, required this.walletType});
|
||||
|
||||
final Map<String, String> urqr;
|
||||
final WalletType walletType;
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_URQRState createState() => _URQRState();
|
||||
}
|
||||
|
||||
const urFrameTime = 1000 ~/ 5;
|
||||
|
||||
class _URQRState extends State<URQR> {
|
||||
Timer? t;
|
||||
int frame = 0;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setState(() {
|
||||
t = Timer.periodic(const Duration(milliseconds: urFrameTime), (timer) {
|
||||
_nextFrame();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _nextFrame() {
|
||||
setState(() {
|
||||
frame++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
t?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
late String selected =
|
||||
(widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first;
|
||||
int selectedInt = 0;
|
||||
|
||||
List<String> get frames {
|
||||
return widget.urqr[selected]?.split("\n") ?? [];
|
||||
}
|
||||
|
||||
late String nextLabel =
|
||||
widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length];
|
||||
|
||||
void next() {
|
||||
final keys = widget.urqr.keys.toList();
|
||||
setState(() {
|
||||
selectedInt++;
|
||||
nextLabel = keys[(selectedInt + 1) % keys.length];
|
||||
selected = keys[(selectedInt) % keys.length];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Container(
|
||||
child: QrImage(
|
||||
data: frames[frame % frames.length],
|
||||
version: -1,
|
||||
size: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.urqr.values.length > 1)
|
||||
widget.walletType == WalletType.monero
|
||||
? _legacySwitch(context)
|
||||
: _newSwitch(context),
|
||||
if (FeatureFlag.hasDevOptions) ...{
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(
|
||||
text:
|
||||
"""Current frame (${frame % frames.length}): ${frames[frame % frames.length]},
|
||||
All frames:
|
||||
- ${frames.join("\n - ")}"""));
|
||||
},
|
||||
child: Text("[dev] copy debug info"),
|
||||
),
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _legacySwitch(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: PrimaryButton(
|
||||
onPressed: next,
|
||||
text: nextLabel,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _newSwitch(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 32),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.qr_code,
|
||||
color: Theme.of(context).colorScheme.primary, size: 24),
|
||||
SizedBox(width: 4),
|
||||
Text("QR Code Format",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () => _showFormatSelectionDialog(context),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
if (_getCurrentFormatName().startsWith('Cupcake'))
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/cupcake.svg',
|
||||
width: 40,
|
||||
height: 40,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_getCurrentFormatName(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_getCurrentFormatSubtitle(),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(0.4),
|
||||
size: 24,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
onTap: () => _showInfoDialog(context),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"What's this for?",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getCurrentFormatName() {
|
||||
if (widget.urqr.isEmpty) return "Unknown";
|
||||
final currentKey =
|
||||
widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
return currentKey.split(' ')[0];
|
||||
}
|
||||
|
||||
String _getCurrentFormatSubtitle() {
|
||||
if (widget.urqr.isEmpty) return "";
|
||||
final currentKey =
|
||||
widget.urqr.keys.elementAt(selectedInt % widget.urqr.length);
|
||||
return currentKey.substring(currentKey.indexOf(' ') + 1);
|
||||
}
|
||||
|
||||
Future<void> _showFormatSelectionDialog(BuildContext context) async {
|
||||
if (widget.urqr.length <= 1) return;
|
||||
|
||||
final keys = widget.urqr.keys.toList();
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return QRFormatSelectionDialog(
|
||||
formats: keys,
|
||||
currentIndex: selectedInt,
|
||||
onFormatSelected: (index) {
|
||||
Navigator.pop(context);
|
||||
setState(() {
|
||||
selectedInt = index;
|
||||
selected = keys[index];
|
||||
nextLabel = keys[(index + 1) % keys.length];
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _showInfoDialog(BuildContext context) async {
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return QRFormatInfoBottomSheet();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user