Khoren Markosyan
3 years ago
26 changed files with 1701 additions and 25 deletions
@ -0,0 +1,62 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:mobx/mobx.dart'; |
||||||
|
import 'package:nb_utils/nb_utils.dart'; |
||||||
|
|
||||||
|
part 'app_store.g.dart'; |
||||||
|
|
||||||
|
AppStore appStore = AppStore(); |
||||||
|
|
||||||
|
class AppStore = AppStoreBase with _$AppStore; |
||||||
|
|
||||||
|
const themeModePref = 'themeModePref'; |
||||||
|
const colorSchemeIndexPref = 'colorSchemeIndexPref'; |
||||||
|
const isSoundOnPref = 'isSoundOnPref'; |
||||||
|
const isVibrationOnPref = 'isVibrationOnPref'; |
||||||
|
const languagePref = 'languagePref'; |
||||||
|
|
||||||
|
abstract class AppStoreBase with Store { |
||||||
|
@observable |
||||||
|
ThemeMode themeMode = ThemeMode.system; |
||||||
|
|
||||||
|
@observable |
||||||
|
int colorSchemeIndex = 4; |
||||||
|
|
||||||
|
@observable |
||||||
|
bool isSoundOn = true; |
||||||
|
|
||||||
|
@observable |
||||||
|
bool isVibrationOn = true; |
||||||
|
|
||||||
|
@observable |
||||||
|
String selectedLanguage = 'en_US'; |
||||||
|
|
||||||
|
@action |
||||||
|
Future<void> setThemeMode(ThemeMode value) async { |
||||||
|
themeMode = value; |
||||||
|
setValue(themeModePref, themeMode.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@action |
||||||
|
Future<void> setColorSchemeIndex(int value) async { |
||||||
|
colorSchemeIndex = value; |
||||||
|
await setValue(colorSchemeIndexPref, colorSchemeIndex); |
||||||
|
} |
||||||
|
|
||||||
|
@action |
||||||
|
Future<void> toggleSoundMode({bool? value}) async { |
||||||
|
isSoundOn = value ?? !isSoundOn; |
||||||
|
setValue(isSoundOnPref, isSoundOn); |
||||||
|
} |
||||||
|
|
||||||
|
@action |
||||||
|
Future<void> toggleVibrationMode({bool? value}) async { |
||||||
|
isVibrationOn = value ?? !isVibrationOn; |
||||||
|
setValue(isVibrationOnPref, isVibrationOn); |
||||||
|
} |
||||||
|
|
||||||
|
@action |
||||||
|
Future<void> setLanguage(String aLanguage) async { |
||||||
|
selectedLanguage = aLanguage; |
||||||
|
await setValue(languagePref, aLanguage); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
|
||||||
|
part of 'app_store.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// StoreGenerator |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic |
||||||
|
|
||||||
|
mixin _$AppStore on AppStoreBase, Store { |
||||||
|
final _$themeModeAtom = Atom(name: 'AppStoreBase.themeMode'); |
||||||
|
|
||||||
|
@override |
||||||
|
ThemeMode get themeMode { |
||||||
|
_$themeModeAtom.reportRead(); |
||||||
|
return super.themeMode; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
set themeMode(ThemeMode value) { |
||||||
|
_$themeModeAtom.reportWrite(value, super.themeMode, () { |
||||||
|
super.themeMode = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
final _$colorSchemeIndexAtom = Atom(name: 'AppStoreBase.colorSchemeIndex'); |
||||||
|
|
||||||
|
@override |
||||||
|
int get colorSchemeIndex { |
||||||
|
_$colorSchemeIndexAtom.reportRead(); |
||||||
|
return super.colorSchemeIndex; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
set colorSchemeIndex(int value) { |
||||||
|
_$colorSchemeIndexAtom.reportWrite(value, super.colorSchemeIndex, () { |
||||||
|
super.colorSchemeIndex = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
final _$isSoundOnAtom = Atom(name: 'AppStoreBase.isSoundOn'); |
||||||
|
|
||||||
|
@override |
||||||
|
bool get isSoundOn { |
||||||
|
_$isSoundOnAtom.reportRead(); |
||||||
|
return super.isSoundOn; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
set isSoundOn(bool value) { |
||||||
|
_$isSoundOnAtom.reportWrite(value, super.isSoundOn, () { |
||||||
|
super.isSoundOn = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
final _$isVibrationOnAtom = Atom(name: 'AppStoreBase.isVibrationOn'); |
||||||
|
|
||||||
|
@override |
||||||
|
bool get isVibrationOn { |
||||||
|
_$isVibrationOnAtom.reportRead(); |
||||||
|
return super.isVibrationOn; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
set isVibrationOn(bool value) { |
||||||
|
_$isVibrationOnAtom.reportWrite(value, super.isVibrationOn, () { |
||||||
|
super.isVibrationOn = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
final _$selectedLanguageAtom = Atom(name: 'AppStoreBase.selectedLanguage'); |
||||||
|
|
||||||
|
@override |
||||||
|
String get selectedLanguage { |
||||||
|
_$selectedLanguageAtom.reportRead(); |
||||||
|
return super.selectedLanguage; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
set selectedLanguage(String value) { |
||||||
|
_$selectedLanguageAtom.reportWrite(value, super.selectedLanguage, () { |
||||||
|
super.selectedLanguage = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
final _$setThemeModeAsyncAction = AsyncAction('AppStoreBase.setThemeMode'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> setThemeMode(ThemeMode value) { |
||||||
|
return _$setThemeModeAsyncAction.run(() => super.setThemeMode(value)); |
||||||
|
} |
||||||
|
|
||||||
|
final _$setColorSchemeIndexAsyncAction = |
||||||
|
AsyncAction('AppStoreBase.setColorSchemeIndex'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> setColorSchemeIndex(int value) { |
||||||
|
return _$setColorSchemeIndexAsyncAction |
||||||
|
.run(() => super.setColorSchemeIndex(value)); |
||||||
|
} |
||||||
|
|
||||||
|
final _$toggleSoundModeAsyncAction = |
||||||
|
AsyncAction('AppStoreBase.toggleSoundMode'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> toggleSoundMode({bool? value}) { |
||||||
|
return _$toggleSoundModeAsyncAction |
||||||
|
.run(() => super.toggleSoundMode(value: value)); |
||||||
|
} |
||||||
|
|
||||||
|
final _$toggleVibrationModeAsyncAction = |
||||||
|
AsyncAction('AppStoreBase.toggleVibrationMode'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> toggleVibrationMode({bool? value}) { |
||||||
|
return _$toggleVibrationModeAsyncAction |
||||||
|
.run(() => super.toggleVibrationMode(value: value)); |
||||||
|
} |
||||||
|
|
||||||
|
final _$setLanguageAsyncAction = AsyncAction('AppStoreBase.setLanguage'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<void> setLanguage(String aLanguage) { |
||||||
|
return _$setLanguageAsyncAction.run(() => super.setLanguage(aLanguage)); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
String toString() { |
||||||
|
return ''' |
||||||
|
themeMode: ${themeMode}, |
||||||
|
colorSchemeIndex: ${colorSchemeIndex}, |
||||||
|
isSoundOn: ${isSoundOn}, |
||||||
|
isVibrationOn: ${isVibrationOn}, |
||||||
|
selectedLanguage: ${selectedLanguage} |
||||||
|
'''; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
import 'package:flex_color_scheme/flex_color_scheme.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
import 'app_store.dart'; |
||||||
|
|
||||||
|
class AppTheme { |
||||||
|
AppTheme._(); |
||||||
|
|
||||||
|
static ThemeData flexLightTheme() => FlexThemeData.light( |
||||||
|
colors: FlexColor.schemesList[appStore.colorSchemeIndex].light, |
||||||
|
tabBarStyle: FlexTabBarStyle.forAppBar, |
||||||
|
surfaceMode: FlexSurfaceMode.highScaffoldLevelSurface, |
||||||
|
blendLevel: 12, |
||||||
|
); |
||||||
|
|
||||||
|
static ThemeData flexDarkTheme() => FlexThemeData.dark( |
||||||
|
colors: FlexColor.schemesList[appStore.colorSchemeIndex].dark, |
||||||
|
tabBarStyle: FlexTabBarStyle.forAppBar, |
||||||
|
surfaceMode: FlexSurfaceMode.highScaffoldLevelSurface, |
||||||
|
blendLevel: 6, |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
const String appName = 'ZxScanner'; |
||||||
|
|
||||||
|
/// space between widgets |
||||||
|
const spaceSmall = 2.0; |
||||||
|
const spaceSmall2 = 4.0; |
||||||
|
const spaceMedium = 8.0; |
||||||
|
const spaceDefault = 16.0; |
||||||
|
const spaceLarge = 24.0; |
||||||
|
const spaceLarge2 = 32.0; |
||||||
|
const spaceLarge3 = 40.0; |
||||||
|
const spaceLarge4 = 96.0; |
||||||
|
|
||||||
|
const spaceMaxWidth = 736.0; |
@ -0,0 +1,62 @@ |
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart |
||||||
|
// This is a library that looks up messages for specific locales by |
||||||
|
// delegating to the appropriate library. |
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file. |
||||||
|
// ignore_for_file:implementation_imports, file_names, unnecessary_new |
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering |
||||||
|
// ignore_for_file:argument_type_not_assignable, invalid_assignment |
||||||
|
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases |
||||||
|
// ignore_for_file:comment_references |
||||||
|
|
||||||
|
import 'dart:async'; |
||||||
|
|
||||||
|
import 'package:intl/intl.dart'; |
||||||
|
import 'package:intl/message_lookup_by_library.dart'; |
||||||
|
import 'package:intl/src/intl_helpers.dart'; |
||||||
|
|
||||||
|
import 'messages_en_US.dart' as messages_en_us; |
||||||
|
|
||||||
|
typedef Future<dynamic> LibraryLoader(); |
||||||
|
Map<String, LibraryLoader> _deferredLibraries = { |
||||||
|
'en_US': () => new Future.value(null), |
||||||
|
}; |
||||||
|
|
||||||
|
MessageLookupByLibrary? _findExact(String localeName) { |
||||||
|
switch (localeName) { |
||||||
|
case 'en_US': |
||||||
|
return messages_en_us.messages; |
||||||
|
default: |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// User programs should call this before using [localeName] for messages. |
||||||
|
Future<bool> initializeMessages(String localeName) async { |
||||||
|
var availableLocale = Intl.verifiedLocale( |
||||||
|
localeName, (locale) => _deferredLibraries[locale] != null, |
||||||
|
onFailure: (_) => null); |
||||||
|
if (availableLocale == null) { |
||||||
|
return new Future.value(false); |
||||||
|
} |
||||||
|
var lib = _deferredLibraries[availableLocale]; |
||||||
|
await (lib == null ? new Future.value(false) : lib()); |
||||||
|
initializeInternalMessageLookup(() => new CompositeMessageLookup()); |
||||||
|
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); |
||||||
|
return new Future.value(true); |
||||||
|
} |
||||||
|
|
||||||
|
bool _messagesExistFor(String locale) { |
||||||
|
try { |
||||||
|
return _findExact(locale) != null; |
||||||
|
} catch (e) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { |
||||||
|
var actualLocale = |
||||||
|
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); |
||||||
|
if (actualLocale == null) return null; |
||||||
|
return _findExact(actualLocale); |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart |
||||||
|
// This is a library that provides messages for a en_US locale. All the |
||||||
|
// messages from the main program should be duplicated here with the same |
||||||
|
// function name. |
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file. |
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new |
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering |
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases |
||||||
|
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes |
||||||
|
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes |
||||||
|
|
||||||
|
import 'package:intl/intl.dart'; |
||||||
|
import 'package:intl/message_lookup_by_library.dart'; |
||||||
|
|
||||||
|
final messages = new MessageLookup(); |
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args); |
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary { |
||||||
|
String get localeName => 'en_US'; |
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages); |
||||||
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{ |
||||||
|
"settingsAppBarTitle": MessageLookupByLibrary.simpleMessage("Settings"), |
||||||
|
"settingsLanguageTitle": |
||||||
|
MessageLookupByLibrary.simpleMessage("Language"), |
||||||
|
"settingsThemeModeTitle": MessageLookupByLibrary.simpleMessage("Theme") |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:intl/intl.dart'; |
||||||
|
import 'intl/messages_all.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// Generator: Flutter Intl IDE plugin |
||||||
|
// Made by Localizely |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars |
||||||
|
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each |
||||||
|
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes |
||||||
|
|
||||||
|
class S { |
||||||
|
S(); |
||||||
|
|
||||||
|
static S? _current; |
||||||
|
|
||||||
|
static S get current { |
||||||
|
assert(_current != null, |
||||||
|
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); |
||||||
|
return _current!; |
||||||
|
} |
||||||
|
|
||||||
|
static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); |
||||||
|
|
||||||
|
static Future<S> load(Locale locale) { |
||||||
|
final name = (locale.countryCode?.isEmpty ?? false) |
||||||
|
? locale.languageCode |
||||||
|
: locale.toString(); |
||||||
|
final localeName = Intl.canonicalizedLocale(name); |
||||||
|
return initializeMessages(localeName).then((_) { |
||||||
|
Intl.defaultLocale = localeName; |
||||||
|
final instance = S(); |
||||||
|
S._current = instance; |
||||||
|
|
||||||
|
return instance; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
static S of(BuildContext context) { |
||||||
|
final instance = S.maybeOf(context); |
||||||
|
assert(instance != null, |
||||||
|
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); |
||||||
|
return instance!; |
||||||
|
} |
||||||
|
|
||||||
|
static S? maybeOf(BuildContext context) { |
||||||
|
return Localizations.of<S>(context, S); |
||||||
|
} |
||||||
|
|
||||||
|
/// `Settings` |
||||||
|
String get settingsAppBarTitle { |
||||||
|
return Intl.message( |
||||||
|
'Settings', |
||||||
|
name: 'settingsAppBarTitle', |
||||||
|
desc: '', |
||||||
|
args: [], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// `Theme` |
||||||
|
String get settingsThemeModeTitle { |
||||||
|
return Intl.message( |
||||||
|
'Theme', |
||||||
|
name: 'settingsThemeModeTitle', |
||||||
|
desc: '', |
||||||
|
args: [], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/// `Language` |
||||||
|
String get settingsLanguageTitle { |
||||||
|
return Intl.message( |
||||||
|
'Language', |
||||||
|
name: 'settingsLanguageTitle', |
||||||
|
desc: '', |
||||||
|
args: [], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> { |
||||||
|
const AppLocalizationDelegate(); |
||||||
|
|
||||||
|
List<Locale> get supportedLocales { |
||||||
|
return const <Locale>[ |
||||||
|
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'), |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
bool isSupported(Locale locale) => _isSupported(locale); |
||||||
|
@override |
||||||
|
Future<S> load(Locale locale) => S.load(locale); |
||||||
|
@override |
||||||
|
bool shouldReload(AppLocalizationDelegate old) => false; |
||||||
|
|
||||||
|
bool _isSupported(Locale locale) { |
||||||
|
for (var supportedLocale in supportedLocales) { |
||||||
|
if (supportedLocale.languageCode == locale.languageCode) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
{ |
||||||
|
"@@locale": "en_US", |
||||||
|
"settingsAppBarTitle": "Settings", |
||||||
|
"settingsThemeModeTitle": "Theme", |
||||||
|
"settingsLanguageTitle": "Language" |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
import 'dart:io'; |
||||||
|
import 'dart:typed_data'; |
||||||
|
|
||||||
|
import 'package:camera/camera.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter/services.dart'; |
||||||
|
import 'package:flutter_zxing/flutter_zxing.dart'; |
||||||
|
import 'package:flutter_zxing/generated_bindings.dart'; |
||||||
|
import 'package:flutter_zxing/zxing_reader_widget.dart'; |
||||||
|
import 'package:flutter_zxing/zxing_writer_widget.dart'; |
||||||
|
import 'package:path_provider/path_provider.dart'; |
||||||
|
import 'package:share_plus/share_plus.dart'; |
||||||
|
|
||||||
|
late Directory tempDir; |
||||||
|
String get tempPath => '${tempDir.path}/zxing.jpg'; |
||||||
|
|
||||||
|
class CreatorPage extends StatefulWidget { |
||||||
|
const CreatorPage({ |
||||||
|
Key? key, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
State<CreatorPage> createState() => _CreatorPageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _CreatorPageState extends State<CreatorPage> |
||||||
|
with TickerProviderStateMixin { |
||||||
|
CameraController? controller; |
||||||
|
TabController? _tabController; |
||||||
|
|
||||||
|
bool isAndroid() => Theme.of(context).platform == TargetPlatform.android; |
||||||
|
|
||||||
|
// Scan result queue |
||||||
|
final _resultQueue = <CodeResult>[]; |
||||||
|
|
||||||
|
// Write result |
||||||
|
Uint8List? writeResult; |
||||||
|
|
||||||
|
// true when the camera is active |
||||||
|
bool _isScanning = true; |
||||||
|
|
||||||
|
@override |
||||||
|
void initState() { |
||||||
|
super.initState(); |
||||||
|
|
||||||
|
initStateAsync(); |
||||||
|
} |
||||||
|
|
||||||
|
void initStateAsync() async { |
||||||
|
_tabController = TabController(length: 3, vsync: this); |
||||||
|
_tabController?.addListener(() { |
||||||
|
_isScanning = _tabController?.index == 0; |
||||||
|
if (_isScanning) { |
||||||
|
controller?.resumePreview(); |
||||||
|
} else { |
||||||
|
controller?.pausePreview(); |
||||||
|
} |
||||||
|
}); |
||||||
|
getTemporaryDirectory().then((value) { |
||||||
|
tempDir = value; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void dispose() { |
||||||
|
super.dispose(); |
||||||
|
controller?.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
void showInSnackBar(String message) {} |
||||||
|
|
||||||
|
void logError(String code, String? message) { |
||||||
|
if (message != null) { |
||||||
|
debugPrint('Error: $code\nError Message: $message'); |
||||||
|
} else { |
||||||
|
debugPrint('Error: $code'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Scaffold( |
||||||
|
appBar: AppBar( |
||||||
|
title: const Text('Creator'), |
||||||
|
bottom: TabBar( |
||||||
|
controller: _tabController, |
||||||
|
tabs: const [ |
||||||
|
Tab(text: 'Scanner'), |
||||||
|
Tab(text: 'Result'), |
||||||
|
Tab(text: 'Writer'), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
body: TabBarView( |
||||||
|
controller: _tabController, |
||||||
|
children: [ |
||||||
|
// Scanner |
||||||
|
ZxingReaderWidget(onScan: (result) async { |
||||||
|
_resultQueue.insert(0, result); |
||||||
|
_tabController?.index = 1; |
||||||
|
await Future.delayed(const Duration(milliseconds: 500)); |
||||||
|
setState(() {}); |
||||||
|
}), |
||||||
|
// Result |
||||||
|
_buildResultList(), |
||||||
|
// Writer |
||||||
|
SingleChildScrollView( |
||||||
|
child: Column( |
||||||
|
children: [ |
||||||
|
ZxingWriterWidget( |
||||||
|
onSuccess: (result) { |
||||||
|
setState(() { |
||||||
|
writeResult = result; |
||||||
|
}); |
||||||
|
}, |
||||||
|
onError: (error) { |
||||||
|
setState(() { |
||||||
|
writeResult = null; |
||||||
|
}); |
||||||
|
ScaffoldMessenger.of(context).hideCurrentSnackBar(); |
||||||
|
ScaffoldMessenger.of(context).showSnackBar( |
||||||
|
SnackBar( |
||||||
|
content: Text( |
||||||
|
error, |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
if (writeResult != null) buildWriteResult(), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
Column buildWriteResult() { |
||||||
|
return Column( |
||||||
|
children: [ |
||||||
|
// Barcode image |
||||||
|
Image.memory(writeResult ?? Uint8List(0)), |
||||||
|
// Share button |
||||||
|
ElevatedButton( |
||||||
|
onPressed: () { |
||||||
|
// Save image to device |
||||||
|
final file = File(tempPath); |
||||||
|
file.writeAsBytesSync(writeResult ?? Uint8List(0)); |
||||||
|
final path = file.path; |
||||||
|
// Share image |
||||||
|
Share.shareFiles([path]); |
||||||
|
}, |
||||||
|
child: const Text('Share'), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
_buildResultList() { |
||||||
|
return _resultQueue.isEmpty |
||||||
|
? const Center( |
||||||
|
child: Text( |
||||||
|
'No Results', |
||||||
|
style: TextStyle(fontSize: 24), |
||||||
|
)) |
||||||
|
: ListView.builder( |
||||||
|
itemCount: _resultQueue.length, |
||||||
|
itemBuilder: (context, index) { |
||||||
|
final result = _resultQueue[index]; |
||||||
|
return ListTile( |
||||||
|
title: Text(result.textString), |
||||||
|
subtitle: Text(result.formatString), |
||||||
|
trailing: ButtonBar( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
// Copy button |
||||||
|
TextButton( |
||||||
|
child: const Text('Copy'), |
||||||
|
onPressed: () { |
||||||
|
Clipboard.setData( |
||||||
|
ClipboardData(text: result.textString)); |
||||||
|
}, |
||||||
|
), |
||||||
|
// Remove button |
||||||
|
IconButton( |
||||||
|
icon: const Icon(Icons.delete, color: Colors.red), |
||||||
|
onPressed: () { |
||||||
|
_resultQueue.removeAt(index); |
||||||
|
setState(() {}); |
||||||
|
}, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
import 'package:convex_bottom_bar/convex_bottom_bar.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/creator_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/history_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/scanner_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/settings_page.dart'; |
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; |
||||||
|
|
||||||
|
class HomePage extends StatefulWidget { |
||||||
|
const HomePage({Key? key}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
State<HomePage> createState() => _HomePageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _HomePageState extends State<HomePage> { |
||||||
|
var selectedIndex = 2; |
||||||
|
|
||||||
|
final creatorPage = const CreatorPage(); |
||||||
|
final historyPage = const HistoryPage(); |
||||||
|
final scannerPage = const ScannerPage(); |
||||||
|
final notificationsPage = const ScannerPage(); |
||||||
|
final settingsPage = const SettingsPage(); |
||||||
|
|
||||||
|
dynamic pages() => [ |
||||||
|
creatorPage, |
||||||
|
historyPage, |
||||||
|
scannerPage, |
||||||
|
notificationsPage, |
||||||
|
settingsPage, |
||||||
|
]; |
||||||
|
|
||||||
|
dynamic tabItems() => [ |
||||||
|
const TabItem(icon: FontAwesomeIcons.barcode), |
||||||
|
const TabItem(icon: FontAwesomeIcons.clockRotateLeft), |
||||||
|
const TabItem(icon: Icons.qr_code_scanner), |
||||||
|
const TabItem(icon: FontAwesomeIcons.solidBell), |
||||||
|
const TabItem(icon: FontAwesomeIcons.gear), |
||||||
|
]; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Scaffold( |
||||||
|
body: pages()[selectedIndex], |
||||||
|
bottomNavigationBar: ConvexAppBar( |
||||||
|
style: TabStyle.fixedCircle, |
||||||
|
backgroundColor: Theme.of(context).bottomAppBarColor, |
||||||
|
color: Theme.of(context).unselectedWidgetColor, |
||||||
|
activeColor: Theme.of(context).colorScheme.primary, |
||||||
|
items: tabItems(), |
||||||
|
initialActiveIndex: selectedIndex, |
||||||
|
onTap: (index) { |
||||||
|
setState(() { |
||||||
|
selectedIndex = index; |
||||||
|
}); |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing/zxing_reader_widget.dart'; |
||||||
|
|
||||||
|
class ScannerPage extends StatefulWidget { |
||||||
|
const ScannerPage({ |
||||||
|
Key? key, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
State<ScannerPage> createState() => _ScannerPageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _ScannerPageState extends State<ScannerPage> |
||||||
|
with TickerProviderStateMixin { |
||||||
|
TabController? _tabController; |
||||||
|
|
||||||
|
@override |
||||||
|
void initState() { |
||||||
|
super.initState(); |
||||||
|
|
||||||
|
initStateAsync(); |
||||||
|
} |
||||||
|
|
||||||
|
void initStateAsync() async { |
||||||
|
_tabController = TabController(length: 3, vsync: this); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void dispose() { |
||||||
|
super.dispose(); |
||||||
|
_tabController?.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Scaffold( |
||||||
|
appBar: AppBar( |
||||||
|
title: const Text('Scanner'), |
||||||
|
bottom: TabBar( |
||||||
|
controller: _tabController, |
||||||
|
tabs: const [ |
||||||
|
Tab(text: 'Single code'), |
||||||
|
Tab(text: 'Multi code'), |
||||||
|
Tab(text: 'Image'), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
body: TabBarView( |
||||||
|
controller: _tabController, |
||||||
|
children: [ |
||||||
|
// Single code scanner |
||||||
|
ZxingReaderWidget(onScan: (result) async { |
||||||
|
// _resultQueue.insert(0, result); |
||||||
|
// await Future.delayed(const Duration(milliseconds: 500)); |
||||||
|
// setState(() {}); |
||||||
|
}), |
||||||
|
// Multi code scanner |
||||||
|
Container(), |
||||||
|
// ZxingReaderWidget(onScan: (result) async { |
||||||
|
// // _resultQueue.insert(0, result); |
||||||
|
// // await Future.delayed(const Duration(milliseconds: 500)); |
||||||
|
// // setState(() {}); |
||||||
|
// }), |
||||||
|
// Image scanner |
||||||
|
Container(), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/configs/app_store.dart'; |
||||||
|
import 'package:flutter_zxing_example/configs/constants.dart'; |
||||||
|
import 'package:flutter_zxing_example/generated/l10n.dart'; |
||||||
|
import 'package:flutter_zxing_example/widgets/common_widgets.dart'; |
||||||
|
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; |
||||||
|
|
||||||
|
import '../widgets/setting_tile.dart'; |
||||||
|
import '../widgets/language_widget.dart'; |
||||||
|
import '../widgets/theme_mode_switch.dart'; |
||||||
|
import '../widgets/theme_selector.dart'; |
||||||
|
|
||||||
|
class SettingsPage extends StatefulWidget { |
||||||
|
const SettingsPage({Key? key}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
State<SettingsPage> createState() => _SettingsPageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _SettingsPageState extends State<SettingsPage> { |
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Scaffold( |
||||||
|
appBar: AppBar( |
||||||
|
title: Text(S.current.settingsAppBarTitle), |
||||||
|
), |
||||||
|
body: SingleChildScrollView( |
||||||
|
child: ContainerX( |
||||||
|
child: Column( |
||||||
|
children: [ |
||||||
|
const SizedBox(height: spaceDefault), |
||||||
|
const Padding( |
||||||
|
padding: EdgeInsets.symmetric(horizontal: spaceDefault), |
||||||
|
child: ThemeSelector(), |
||||||
|
), |
||||||
|
SettingTile( |
||||||
|
context, |
||||||
|
leading: Icons.brightness_4, |
||||||
|
title: S.current.settingsThemeModeTitle, |
||||||
|
trailing: ThemeModeSwitch( |
||||||
|
themeMode: appStore.themeMode, |
||||||
|
onChanged: (mode) { |
||||||
|
appStore.setThemeMode(mode); |
||||||
|
setState(() {}); |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
SettingTile( |
||||||
|
context, |
||||||
|
leading: FontAwesomeIcons.globe, |
||||||
|
title: S.current.settingsLanguageTitle, |
||||||
|
trailing: LanguageWidget( |
||||||
|
onChanged: (value) { |
||||||
|
setState(() {}); |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
import 'dart:ui'; |
||||||
|
|
||||||
|
extension LocaleParsing on String { |
||||||
|
Locale parseLocale() { |
||||||
|
assert(contains('_') == true); |
||||||
|
var languageCode = split('_').first; |
||||||
|
var countryCode = split('_').last; |
||||||
|
return Locale.fromSubtags( |
||||||
|
languageCode: languageCode, countryCode: countryCode); |
||||||
|
} |
||||||
|
|
||||||
|
String toLangIcon() { |
||||||
|
assert(length == 2); |
||||||
|
switch (toLowerCase()) { |
||||||
|
case 'us': |
||||||
|
case 'en': |
||||||
|
return '🇺🇸'; |
||||||
|
case 'ru': |
||||||
|
return '🇷🇺'; |
||||||
|
case 'am': |
||||||
|
case 'hy': |
||||||
|
return '🇦🇲'; |
||||||
|
default: |
||||||
|
return '🇺🇸'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String toLangName() { |
||||||
|
assert(length == 2); |
||||||
|
switch (toLowerCase()) { |
||||||
|
case 'us': |
||||||
|
case 'en': |
||||||
|
return toLangIcon() + ' English'; |
||||||
|
case 'ru': |
||||||
|
return toLangIcon() + ' Русский'; |
||||||
|
case 'am': |
||||||
|
case 'hy': |
||||||
|
return toLangIcon() + ' Հայերեն'; |
||||||
|
default: |
||||||
|
return toLangIcon() + ' English'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String toLangCode() { |
||||||
|
assert(contains('_') == true); |
||||||
|
return split('_').first; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/creator_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/history_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/home_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/scanner_page.dart'; |
||||||
|
import 'package:flutter_zxing_example/pages/settings_page.dart'; |
||||||
|
|
||||||
|
abstract class AppRoutes { |
||||||
|
static const creator = '/creator'; |
||||||
|
static const history = '/history'; |
||||||
|
static const home = '/'; |
||||||
|
static const scanner = '/scanner'; |
||||||
|
static const settings = '/settings'; |
||||||
|
} |
||||||
|
|
||||||
|
class AppRouter { |
||||||
|
Route onGenerateRoute(RouteSettings settings) { |
||||||
|
switch (settings.name) { |
||||||
|
case AppRoutes.creator: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => const CreatorPage(), |
||||||
|
); |
||||||
|
case AppRoutes.history: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => const HistoryPage(), |
||||||
|
); |
||||||
|
case AppRoutes.home: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => const HomePage(), |
||||||
|
); |
||||||
|
case AppRoutes.scanner: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => const ScannerPage(), |
||||||
|
); |
||||||
|
case AppRoutes.settings: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => const SettingsPage(), |
||||||
|
); |
||||||
|
default: |
||||||
|
return MaterialPageRoute( |
||||||
|
builder: (_) => Container(), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import 'package:flutter/gestures.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
class MyCustomScrollBehavior extends MaterialScrollBehavior { |
||||||
|
@override |
||||||
|
Set<PointerDeviceKind> get dragDevices => { |
||||||
|
PointerDeviceKind.touch, |
||||||
|
PointerDeviceKind.mouse, |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/configs/constants.dart'; |
||||||
|
|
||||||
|
class ContainerX extends StatelessWidget { |
||||||
|
const ContainerX({Key? key, this.child}) : super(key: key); |
||||||
|
|
||||||
|
final Widget? child; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Center( |
||||||
|
child: Container( |
||||||
|
constraints: const BoxConstraints(maxWidth: spaceMaxWidth), |
||||||
|
child: child, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/configs/app_store.dart'; |
||||||
|
import 'package:flutter_zxing_example/generated/l10n.dart'; |
||||||
|
import 'package:flutter_zxing_example/utils/extensions.dart'; |
||||||
|
|
||||||
|
class LanguageWidget extends StatelessWidget { |
||||||
|
const LanguageWidget({Key? key, required this.onChanged}) : super(key: key); |
||||||
|
|
||||||
|
final Function(Locale) onChanged; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return DropdownButton<Locale>( |
||||||
|
value: appStore.selectedLanguage.parseLocale(), |
||||||
|
isExpanded: false, |
||||||
|
isDense: false, |
||||||
|
underline: const SizedBox(), |
||||||
|
onChanged: (value) async { |
||||||
|
if (value != null) { |
||||||
|
await S.load(value); |
||||||
|
appStore.setLanguage(value.toString()); |
||||||
|
onChanged(value); |
||||||
|
} |
||||||
|
}, |
||||||
|
selectedItemBuilder: (context) { |
||||||
|
return S.delegate.supportedLocales |
||||||
|
.map((e) => DropdownMenuItem<Locale>( |
||||||
|
value: e, |
||||||
|
child: SizedBox( |
||||||
|
width: 80, |
||||||
|
child: Text( |
||||||
|
e.countryCode?.toLangIcon() ?? '', |
||||||
|
style: Theme.of(context).textTheme.headline5, |
||||||
|
textAlign: TextAlign.right, |
||||||
|
), |
||||||
|
), |
||||||
|
)) |
||||||
|
.toList(); |
||||||
|
}, |
||||||
|
items: S.delegate.supportedLocales |
||||||
|
.map((e) => DropdownMenuItem( |
||||||
|
value: e, |
||||||
|
child: Text(e.countryCode?.toLangName() ?? ''), |
||||||
|
)) |
||||||
|
.toList(), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
class SettingTile extends StatelessWidget { |
||||||
|
const SettingTile( |
||||||
|
this.context, { |
||||||
|
Key? key, |
||||||
|
this.leading, |
||||||
|
this.title, |
||||||
|
this.trailing, |
||||||
|
this.onTap, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
final BuildContext context; |
||||||
|
final IconData? leading; |
||||||
|
final String? title; |
||||||
|
final Widget? trailing; |
||||||
|
final Function()? onTap; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Card( |
||||||
|
child: ListTile( |
||||||
|
leading: leading != null |
||||||
|
? Icon( |
||||||
|
leading, |
||||||
|
color: Theme.of(context).colorScheme.secondary, |
||||||
|
) |
||||||
|
: null, |
||||||
|
title: Text(title ?? ''), |
||||||
|
trailing: trailing, |
||||||
|
onTap: onTap, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
/// Widget using [ToggleButtons) that can be used to toggle the theme mode |
||||||
|
/// of an application. |
||||||
|
/// |
||||||
|
/// This is a simple Flutter "Universal" Widget that only depends on the SDK and |
||||||
|
/// can be dropped into any application. |
||||||
|
class ThemeModeSwitch extends StatelessWidget { |
||||||
|
const ThemeModeSwitch({ |
||||||
|
Key? key, |
||||||
|
required this.themeMode, |
||||||
|
required this.onChanged, |
||||||
|
}) : super(key: key); |
||||||
|
final ThemeMode themeMode; |
||||||
|
final ValueChanged<ThemeMode> onChanged; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
final List<bool> isSelected = <bool>[ |
||||||
|
themeMode == ThemeMode.light, |
||||||
|
themeMode == ThemeMode.system, |
||||||
|
themeMode == ThemeMode.dark, |
||||||
|
]; |
||||||
|
return ToggleButtons( |
||||||
|
isSelected: isSelected, |
||||||
|
onPressed: (int newIndex) { |
||||||
|
if (newIndex == 0) { |
||||||
|
onChanged(ThemeMode.light); |
||||||
|
} else if (newIndex == 1) { |
||||||
|
onChanged(ThemeMode.system); |
||||||
|
} else { |
||||||
|
onChanged(ThemeMode.dark); |
||||||
|
} |
||||||
|
}, |
||||||
|
children: const <Widget>[ |
||||||
|
Icon(Icons.wb_sunny), |
||||||
|
Icon(Icons.phone_iphone), |
||||||
|
Icon(Icons.bedtime), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
import 'package:flex_color_scheme/flex_color_scheme.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_zxing_example/configs/app_store.dart'; |
||||||
|
|
||||||
|
// The width size of the scrolling button. |
||||||
|
const double _kWidthOfScrollItem = 71.6; |
||||||
|
|
||||||
|
// Horizontal theme selector of themes offered in our [AppColor.schemes]. |
||||||
|
// |
||||||
|
// This example uses a StatefulWidget for the scroll controller and |
||||||
|
// index to keep track of previously selected color scheme, so we can animate |
||||||
|
// to the new selection, also done when the themeController schemeIndex is |
||||||
|
// changed via other UI controls, like in the drop box that is also used in |
||||||
|
// this demo as another way to change the color scheme. |
||||||
|
// |
||||||
|
// The theme is controlled via the passed in ThemeController. |
||||||
|
class ThemeSelector extends StatefulWidget { |
||||||
|
const ThemeSelector({ |
||||||
|
Key? key, |
||||||
|
}) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
_ThemeSelectorState createState() => _ThemeSelectorState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _ThemeSelectorState extends State<ThemeSelector> { |
||||||
|
late ScrollController scrollController; |
||||||
|
late int schemeIndex; |
||||||
|
|
||||||
|
@override |
||||||
|
void initState() { |
||||||
|
super.initState(); |
||||||
|
schemeIndex = appStore.colorSchemeIndex; |
||||||
|
scrollController = ScrollController( |
||||||
|
keepScrollOffset: true, |
||||||
|
initialScrollOffset: _kWidthOfScrollItem * schemeIndex, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void dispose() { |
||||||
|
scrollController.dispose(); |
||||||
|
super.dispose(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void didChangeDependencies() { |
||||||
|
// Index got updated in popup and deps changed, animate it to new index. |
||||||
|
if (appStore.colorSchemeIndex != schemeIndex) { |
||||||
|
schemeIndex = appStore.colorSchemeIndex; |
||||||
|
scrollController.animateTo(_kWidthOfScrollItem * schemeIndex, |
||||||
|
duration: const Duration(milliseconds: 350), |
||||||
|
curve: Curves.easeOutCubic); |
||||||
|
} |
||||||
|
super.didChangeDependencies(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
final bool isLight = Theme.of(context).brightness == Brightness.light; |
||||||
|
return SizedBox( |
||||||
|
height: 76, |
||||||
|
child: Row( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: ListView.builder( |
||||||
|
controller: scrollController, |
||||||
|
physics: const ClampingScrollPhysics(), |
||||||
|
scrollDirection: Axis.horizontal, |
||||||
|
itemCount: FlexColor.schemesList.length, |
||||||
|
itemBuilder: (BuildContext context, int index) { |
||||||
|
return FlexThemeModeOptionButton( |
||||||
|
optionButtonBorderRadius: 12, |
||||||
|
height: 32, |
||||||
|
width: 32, |
||||||
|
padding: const EdgeInsets.all(0.4), |
||||||
|
optionButtonMargin: EdgeInsets.zero, |
||||||
|
borderRadius: 0, |
||||||
|
unselectedBorder: BorderSide.none, |
||||||
|
selectedBorder: BorderSide( |
||||||
|
color: Theme.of(context).primaryColorLight, |
||||||
|
width: 5, |
||||||
|
), |
||||||
|
onSelect: () { |
||||||
|
scrollController.animateTo( |
||||||
|
_kWidthOfScrollItem * index, |
||||||
|
duration: const Duration(milliseconds: 350), |
||||||
|
curve: Curves.easeOutCubic, |
||||||
|
); |
||||||
|
schemeIndex = index; |
||||||
|
appStore.setColorSchemeIndex(index); |
||||||
|
}, |
||||||
|
selected: appStore.colorSchemeIndex == index, |
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface, |
||||||
|
flexSchemeColor: isLight |
||||||
|
? FlexColor.schemesList[index].light |
||||||
|
: FlexColor.schemesList[index].dark, |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue