Foinse foilsithe
Íosluchtaigh foinse ZIP
CH-J SM Mobile
Brabhsáil comhadlanna agus comhaid chun feidhmchlár áirithe a scaoileadh.
CH-J / SM Mobile
134 Comhaid
sm-mobile-20261016T050549Z-3678f2a05fa9
Suimigh: · Tógtha:
1
import 'package:flutter/material.dart';2
import '../core/app_controller.dart';3
import '../core/failure.dart';4
import '../l10n/strings.dart';5
import 'common.dart';7
Widget languagePicker(BuildContext context, AppController c) =>8
DropdownButtonFormField<String>(9
initialValue: c.locale?.languageCode ?? 'system',10
decoration: InputDecoration(labelText: context.tr('language')),11
items: ['system', 'cs', 'de', 'en']12
.map(13
(v) => DropdownMenuItem(14
value: v,15
child: Text(switch (v) {16
'cs' => 'Čeština',17
'de' => 'Deutsch',18
'en' => 'English',19
_ => context.tr('system'),20
}),21
),22
)23
.toList(),24
onChanged: (v) async {25
c.locale = v == 'system' ? null : Locale(v!);26
try {27
await c.saveSettings();28
} catch (e) {29
if (context.mounted) showFailure(context, e);30
}31
},32
);34
class Onboarding extends StatefulWidget {35
final AppController controller;36
const Onboarding(this.controller, {super.key});37
@override38
State<Onboarding> createState() => _OnboardingState();39
}41
class _OnboardingState extends State<Onboarding> {42
int step = 0;43
bool consent = false, busy = false, declined = false;44
@override45
Widget build(BuildContext context) {46
final c = widget.controller;47
final lang = Localizations.localeOf(context).languageCode;48
return Scaffold(49
appBar: AppBar(title: const Text('CH-J · SM Mobile')),50
body: SafeArea(51
child: Center(52
child: ConstrainedBox(53
constraints: const BoxConstraints(maxWidth: 760),54
child: Padding(55
padding: const EdgeInsets.all(20),56
child: LayoutBuilder(57
builder: (context, constraints) => SingleChildScrollView(58
child: SizedBox(59
height: constraints.maxHeight.clamp(60
600 * MediaQuery.textScalerOf(context).scale(1),61
double.infinity,62
),63
child: Column(64
crossAxisAlignment: CrossAxisAlignment.stretch,65
children: [66
if (step == 0) ...[67
Text(68
context.tr('welcome'),69
style: Theme.of(context).textTheme.headlineSmall,70
),71
const SizedBox(height: 16),72
languagePicker(context, c),73
const SizedBox(height: 16),74
Text(context.tr('localOnly')),75
const SizedBox(height: 16),76
],77
Text(78
context.tr(step == 0 ? 'guidance' : 'license'),79
style: Theme.of(context).textTheme.titleLarge,80
),81
const SizedBox(height: 12),82
Expanded(83
child: Card(84
child: SingleChildScrollView(85
padding: const EdgeInsets.all(16),86
child: SelectableText(87
step == 088
? c.legal.guidance[lang] ??89
c.legal.guidance['en']!90
: c.legal.license,91
),92
),93
),94
),95
if (step == 1)96
CheckboxListTile(97
value: consent,98
onChanged: busy99
? null100
: (v) => setState(() => consent = v!),101
title: Text(context.tr('consent')),102
contentPadding: EdgeInsets.zero,103
),104
if (declined)105
Text(106
context.tr('licenseDeclined'),107
style: TextStyle(108
color: Theme.of(context).colorScheme.error,109
),110
),111
Wrap(112
spacing: 12,113
alignment: WrapAlignment.end,114
children: [115
if (step == 1)116
TextButton(117
onPressed: busy118
? null119
: () => setState(() {120
declined = true;121
consent = false;122
}),123
child: Text(context.tr('decline')),124
),125
FilledButton(126
onPressed: busy || (step == 1 && !consent)127
? null128
: () async {129
if (step == 0) {130
setState(() => step = 1);131
return;132
}133
setState(() => busy = true);134
try {135
await c.acceptLicense();136
} catch (e) {137
if (context.mounted) {138
showFailure(context, e);139
}140
} finally {141
if (mounted) {142
setState(() => busy = false);143
}144
}145
},146
child: Text(147
context.tr(148
step == 0 ? 'continueAction' : 'accept',149
),150
),151
),152
],153
),154
],155
),156
),157
),158
),159
),160
),161
),162
),163
);164
}165
}167
class VaultGate extends StatefulWidget {168
final AppController controller;169
const VaultGate(this.controller, {super.key});170
@override171
State<VaultGate> createState() => _VaultGateState();172
}174
class _VaultGateState extends State<VaultGate> {175
final password = TextEditingController(), repeated = TextEditingController();176
bool busy = false;177
Future<void> open({bool biometric = false}) async {178
setState(() => busy = true);179
try {180
final c = widget.controller;181
if (biometric) {182
await c.biometricUnlock(context.tr('biometricReason'));183
} else {184
if (!c.initialized && password.text != repeated.text) {185
throw const Failure('passwordMismatch');186
}187
await c.openVault(password.text, create: !c.initialized);188
}189
password.clear();190
repeated.clear();191
} catch (e) {192
if (mounted) showFailure(context, e);193
} finally {194
if (mounted) setState(() => busy = false);195
}196
}198
@override199
void dispose() {200
password.dispose();201
repeated.dispose();202
super.dispose();203
}205
@override206
Widget build(BuildContext context) => Scaffold(207
appBar: AppBar(title: const Text('SM Mobile')),208
body: SafeArea(209
child: Center(210
child: SingleChildScrollView(211
padding: const EdgeInsets.all(24),212
child: ConstrainedBox(213
constraints: const BoxConstraints(maxWidth: 450),214
child: Column(215
crossAxisAlignment: CrossAxisAlignment.stretch,216
children: [217
Icon(218
Icons.lock_outline,219
size: 56,220
color: Theme.of(context).colorScheme.primary,221
),222
const SizedBox(height: 20),223
Text(224
context.tr(225
widget.controller.initialized ? 'vault' : 'createVault',226
),227
style: Theme.of(context).textTheme.headlineSmall,228
textAlign: TextAlign.center,229
),230
const SizedBox(height: 20),231
if (!widget.controller.initialized) ...[232
Text(context.tr('vaultAdvice')),233
const SizedBox(height: 20),234
],235
TextField(236
controller: password,237
obscureText: true,238
enableSuggestions: false,239
autocorrect: false,240
decoration: InputDecoration(241
labelText: context.tr('masterPassword'),242
),243
onSubmitted: (_) {244
if (!busy) open();245
},246
),247
if (!widget.controller.initialized) ...[248
const SizedBox(height: 16),249
TextField(250
controller: repeated,251
obscureText: true,252
enableSuggestions: false,253
autocorrect: false,254
decoration: InputDecoration(255
labelText: context.tr('confirmPassword'),256
),257
),258
],259
const SizedBox(height: 20),260
FilledButton(261
onPressed: busy ? null : open,262
child: Text(263
context.tr(264
widget.controller.initialized ? 'unlock' : 'createVault',265
),266
),267
),268
if (widget.controller.initialized &&269
widget.controller.biometric)270
OutlinedButton(271
onPressed: busy ? null : () => open(biometric: true),272
child: Text(context.tr('unlockBiometric')),273
),274
if (busy) const LinearProgressIndicator(),275
],276
),277
),278
),279
),280
),281
);282
}SHA-256
66f06d67bd62c622d0039667bfef1c85d46a17766dcf22da716031833a713edbCH-J Proprietary Software License 1.14 · Tá Foinse ar fáil faoin CH-J Proprietary Software License 1.14. Ní chuireann a infhaighteacht a athrú ar na téarmaí ceadúnais nó cearta breise a dheonú.
Cartlann SHA-256
c486a807e4828998b9c57ab0f2e760f7980b31965fe118e89d3e1a5a2b65be65