mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 02:01:36 +03:00
More work on integrating M3 Chips in the UI.
This commit is contained in:
parent
53b1254145
commit
f9067d9bdd
@ -14,6 +14,7 @@ import 'core/state.dart';
|
||||
import 'desktop/state.dart';
|
||||
import 'version.dart';
|
||||
import 'widgets/responsive_dialog.dart';
|
||||
import 'widgets/choice_filter_chip.dart';
|
||||
|
||||
final _log = Logger('about');
|
||||
|
||||
@ -141,8 +142,8 @@ class AboutPage extends ConsumerWidget {
|
||||
const LoggingPanel(),
|
||||
if (isDesktop) ...[
|
||||
const SizedBox(height: 12.0),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(Icons.bug_report_outlined),
|
||||
ActionChip(
|
||||
avatar: const Icon(Icons.bug_report_outlined),
|
||||
label: const Text('Run diagnostics'),
|
||||
onPressed: () async {
|
||||
_log.info('Running diagnostics...');
|
||||
@ -174,31 +175,33 @@ class LoggingPanel extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Column(
|
||||
final logLevel = ref.watch(logLevelProvider);
|
||||
return Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 4.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
const SizedBox(height: 12.0),
|
||||
DropdownButtonFormField<Level>(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Log level',
|
||||
border: OutlineInputBorder(),
|
||||
ChoiceFilterChip<Level>(
|
||||
avatar: Icon(
|
||||
Icons.insights,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
value: ref.watch(logLevelProvider),
|
||||
items: Levels.LEVELS
|
||||
.map((e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(e.name.toUpperCase()),
|
||||
))
|
||||
.toList(),
|
||||
value: logLevel,
|
||||
items: Levels.LEVELS,
|
||||
selected: logLevel != Level.INFO,
|
||||
labelBuilder: (value) => Text(
|
||||
'Log level: ${value.name[0]}${value.name.substring(1).toLowerCase()}'),
|
||||
itemBuilder: (value) =>
|
||||
Text('${value.name[0]}${value.name.substring(1).toLowerCase()}'),
|
||||
onChanged: (level) {
|
||||
ref.read(logLevelProvider.notifier).setLogLevel(level!);
|
||||
ref.read(logLevelProvider.notifier).setLogLevel(level);
|
||||
_log.debug('Log level set to $level');
|
||||
showMessage(context, 'Log level set to $level');
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(Icons.copy),
|
||||
label: const Text('Copy log to clipboard'),
|
||||
ActionChip(
|
||||
avatar: const Icon(Icons.copy),
|
||||
label: const Text('Copy log'),
|
||||
onPressed: () async {
|
||||
_log.info('Copying log to clipboard ($version)...');
|
||||
final logs = await ref.read(logLevelProvider.notifier).getLogs();
|
||||
|
@ -159,6 +159,7 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> {
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isObscure ? Icons.visibility : Icons.visibility_off,
|
||||
color: IconTheme.of(context).color,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
@ -5,12 +5,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:yubico_authenticator/cancellation_exception.dart';
|
||||
|
||||
import '../../app/logging.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../cancellation_exception.dart';
|
||||
import '../../desktop/models.dart';
|
||||
import '../../widgets/choice_filter_chip.dart';
|
||||
import '../../widgets/file_drop_target.dart';
|
||||
@ -57,6 +57,16 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
_scanQrCode(QrScanner qrScanner) async {
|
||||
try {
|
||||
setState(() {
|
||||
// If we have a previous scan result stored, clear it
|
||||
if (_qrState == _QrScanState.success) {
|
||||
_issuerController.text = '';
|
||||
_accountController.text = '';
|
||||
_secretController.text = '';
|
||||
_oathType = defaultOathType;
|
||||
_hashAlgorithm = defaultHashAlgorithm;
|
||||
_periodController.text = '$defaultPeriod';
|
||||
_digits = defaultDigits;
|
||||
}
|
||||
_qrState = _QrScanState.scanning;
|
||||
});
|
||||
final otpauth = await qrScanner.scanQr();
|
||||
@ -266,6 +276,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isObscure ? Icons.visibility : Icons.visibility_off,
|
||||
color: IconTheme.of(context).color,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -293,30 +304,19 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
if (qrScanner != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (_qrState != _QrScanState.scanning) ...[
|
||||
OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
fixedSize: const Size.fromWidth(132)),
|
||||
onPressed: () {
|
||||
_scanQrCode(qrScanner);
|
||||
},
|
||||
icon: const Icon(Icons.qr_code),
|
||||
label: const Text('Scan QR code'),
|
||||
)
|
||||
] else ...[
|
||||
OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
fixedSize: const Size.fromWidth(132)),
|
||||
onPressed: null,
|
||||
child: const SizedBox.square(
|
||||
dimension: 16.0,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
child: ActionChip(
|
||||
autofocus: true,
|
||||
avatar: _qrState != _QrScanState.scanning
|
||||
? (_qrState == _QrScanState.success
|
||||
? const Icon(Icons.qr_code)
|
||||
: const Icon(Icons.qr_code_scanner_outlined))
|
||||
: const CircularProgressIndicator(strokeWidth: 2.0),
|
||||
label: _qrState == _QrScanState.success
|
||||
? const Text('Scanned QR code')
|
||||
: const Text('Scan QR code'),
|
||||
onPressed: () {
|
||||
_scanQrCode(qrScanner);
|
||||
}),
|
||||
),
|
||||
const Divider(),
|
||||
Wrap(
|
||||
@ -337,6 +337,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
ChoiceFilterChip<OathType>(
|
||||
items: OathType.values,
|
||||
value: _oathType,
|
||||
selected: _oathType != defaultOathType,
|
||||
itemBuilder: (value) => Text(value.displayName),
|
||||
onChanged: _qrState != _QrScanState.success
|
||||
? (value) {
|
||||
@ -349,6 +350,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
ChoiceFilterChip<HashAlgorithm>(
|
||||
items: HashAlgorithm.values,
|
||||
value: _hashAlgorithm,
|
||||
selected: _hashAlgorithm != defaultHashAlgorithm,
|
||||
itemBuilder: (value) => Text(value.displayName),
|
||||
onChanged: _qrState != _QrScanState.success
|
||||
? (value) {
|
||||
@ -363,6 +365,8 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
items: _periodValues,
|
||||
value:
|
||||
int.tryParse(_periodController.text) ?? defaultPeriod,
|
||||
selected:
|
||||
int.tryParse(_periodController.text) != defaultPeriod,
|
||||
itemBuilder: ((value) => Text('$value sec')),
|
||||
onChanged: _qrState != _QrScanState.success
|
||||
? (period) {
|
||||
@ -375,6 +379,7 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
|
||||
ChoiceFilterChip<int>(
|
||||
items: _digitsValues,
|
||||
value: _digits,
|
||||
selected: _digits != defaultDigits,
|
||||
itemBuilder: (value) => Text('$value digits'),
|
||||
onChanged: _qrState != _QrScanState.success
|
||||
? (digits) {
|
||||
|
@ -288,6 +288,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> {
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isObscure ? Icons.visibility : Icons.visibility_off,
|
||||
color: IconTheme.of(context).color,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
@ -6,13 +6,19 @@ class ChoiceFilterChip<T> extends StatefulWidget {
|
||||
final T value;
|
||||
final List<T> items;
|
||||
final Widget Function(T value) itemBuilder;
|
||||
final Widget Function(T value)? labelBuilder;
|
||||
final void Function(T value)? onChanged;
|
||||
final Widget? avatar;
|
||||
final bool selected;
|
||||
const ChoiceFilterChip({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.items,
|
||||
required this.itemBuilder,
|
||||
required this.onChanged,
|
||||
this.avatar,
|
||||
this.selected = false,
|
||||
this.labelBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -25,11 +31,12 @@ class _ChoiceFilterChipState<T> extends State<ChoiceFilterChip<T>> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FilterChip(
|
||||
avatar: widget.avatar,
|
||||
labelPadding: const EdgeInsets.only(left: 4),
|
||||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
widget.itemBuilder(widget.value),
|
||||
(widget.labelBuilder ?? widget.itemBuilder).call(widget.value),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: Icon(
|
||||
@ -39,7 +46,7 @@ class _ChoiceFilterChipState<T> extends State<ChoiceFilterChip<T>> {
|
||||
),
|
||||
],
|
||||
),
|
||||
selected: true,
|
||||
selected: widget.selected,
|
||||
showCheckmark: false,
|
||||
onSelected: widget.onChanged != null
|
||||
? (_) async {
|
||||
|
Loading…
Reference in New Issue
Block a user