diff --git a/example/lib/configs/app_store.dart b/example/lib/configs/app_store.dart new file mode 100644 index 0000000..60187cd --- /dev/null +++ b/example/lib/configs/app_store.dart @@ -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 setThemeMode(ThemeMode value) async { + themeMode = value; + setValue(themeModePref, themeMode.toString()); + } + + @action + Future setColorSchemeIndex(int value) async { + colorSchemeIndex = value; + await setValue(colorSchemeIndexPref, colorSchemeIndex); + } + + @action + Future toggleSoundMode({bool? value}) async { + isSoundOn = value ?? !isSoundOn; + setValue(isSoundOnPref, isSoundOn); + } + + @action + Future toggleVibrationMode({bool? value}) async { + isVibrationOn = value ?? !isVibrationOn; + setValue(isVibrationOnPref, isVibrationOn); + } + + @action + Future setLanguage(String aLanguage) async { + selectedLanguage = aLanguage; + await setValue(languagePref, aLanguage); + } +} diff --git a/example/lib/configs/app_store.g.dart b/example/lib/configs/app_store.g.dart new file mode 100644 index 0000000..012c84e --- /dev/null +++ b/example/lib/configs/app_store.g.dart @@ -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 setThemeMode(ThemeMode value) { + return _$setThemeModeAsyncAction.run(() => super.setThemeMode(value)); + } + + final _$setColorSchemeIndexAsyncAction = + AsyncAction('AppStoreBase.setColorSchemeIndex'); + + @override + Future setColorSchemeIndex(int value) { + return _$setColorSchemeIndexAsyncAction + .run(() => super.setColorSchemeIndex(value)); + } + + final _$toggleSoundModeAsyncAction = + AsyncAction('AppStoreBase.toggleSoundMode'); + + @override + Future toggleSoundMode({bool? value}) { + return _$toggleSoundModeAsyncAction + .run(() => super.toggleSoundMode(value: value)); + } + + final _$toggleVibrationModeAsyncAction = + AsyncAction('AppStoreBase.toggleVibrationMode'); + + @override + Future toggleVibrationMode({bool? value}) { + return _$toggleVibrationModeAsyncAction + .run(() => super.toggleVibrationMode(value: value)); + } + + final _$setLanguageAsyncAction = AsyncAction('AppStoreBase.setLanguage'); + + @override + Future setLanguage(String aLanguage) { + return _$setLanguageAsyncAction.run(() => super.setLanguage(aLanguage)); + } + + @override + String toString() { + return ''' +themeMode: ${themeMode}, +colorSchemeIndex: ${colorSchemeIndex}, +isSoundOn: ${isSoundOn}, +isVibrationOn: ${isVibrationOn}, +selectedLanguage: ${selectedLanguage} + '''; + } +} diff --git a/example/lib/configs/app_theme.dart b/example/lib/configs/app_theme.dart new file mode 100644 index 0000000..69d2b11 --- /dev/null +++ b/example/lib/configs/app_theme.dart @@ -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, + ); +} diff --git a/example/lib/configs/constants.dart b/example/lib/configs/constants.dart new file mode 100644 index 0000000..74bf122 --- /dev/null +++ b/example/lib/configs/constants.dart @@ -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; diff --git a/example/lib/generated/intl/messages_all.dart b/example/lib/generated/intl/messages_all.dart new file mode 100644 index 0000000..0895730 --- /dev/null +++ b/example/lib/generated/intl/messages_all.dart @@ -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 LibraryLoader(); +Map _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 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); +} diff --git a/example/lib/generated/intl/messages_en_US.dart b/example/lib/generated/intl/messages_en_US.dart new file mode 100644 index 0000000..1c42aca --- /dev/null +++ b/example/lib/generated/intl/messages_en_US.dart @@ -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 args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en_US'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "settingsAppBarTitle": MessageLookupByLibrary.simpleMessage("Settings"), + "settingsLanguageTitle": + MessageLookupByLibrary.simpleMessage("Language"), + "settingsThemeModeTitle": MessageLookupByLibrary.simpleMessage("Theme") + }; +} diff --git a/example/lib/generated/l10n.dart b/example/lib/generated/l10n.dart new file mode 100644 index 0000000..53f722d --- /dev/null +++ b/example/lib/generated/l10n.dart @@ -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 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(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 { + const AppLocalizationDelegate(); + + List get supportedLocales { + return const [ + Locale.fromSubtags(languageCode: 'en', countryCode: 'US'), + ]; + } + + @override + bool isSupported(Locale locale) => _isSupported(locale); + @override + Future 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; + } +} diff --git a/example/lib/l10n/intl_en_US.arb b/example/lib/l10n/intl_en_US.arb new file mode 100644 index 0000000..904eaf1 --- /dev/null +++ b/example/lib/l10n/intl_en_US.arb @@ -0,0 +1,6 @@ +{ + "@@locale": "en_US", + "settingsAppBarTitle": "Settings", + "settingsThemeModeTitle": "Theme", + "settingsLanguageTitle": "Language" +} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 53582a0..979477b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,11 +1,46 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:flutter_zxing_example/utils/extensions.dart'; +import 'package:nb_utils/nb_utils.dart'; -import 'package:flutter_zxing_example/zxing_page.dart'; +import 'configs/app_store.dart'; +import 'configs/app_theme.dart'; +import 'generated/l10n.dart' as loc; +import 'utils/router.dart'; +import 'utils/scroll_behavior.dart'; -void main() { +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await _initializeAppStore(); runApp(const MyApp()); } +_initializeAppStore() async { + await initialize(); + final themeModeString = getStringAsync( + themeModePref, + defaultValue: appStore.themeMode.toString(), + ); + await appStore.setThemeMode( + ThemeMode.values + .firstWhere((element) => element.toString() == themeModeString), + ); + await appStore.setColorSchemeIndex( + getIntAsync(colorSchemeIndexPref, defaultValue: appStore.colorSchemeIndex), + ); + await appStore.toggleSoundMode( + value: getBoolAsync(isSoundOnPref, defaultValue: appStore.isSoundOn), + ); + await appStore.toggleVibrationMode( + value: + getBoolAsync(isVibrationOnPref, defaultValue: appStore.isVibrationOn), + ); + await appStore.setLanguage( + getStringAsync(languagePref, defaultValue: appStore.selectedLanguage), + ); +} + class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @@ -14,15 +49,28 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - @override - void initState() { - super.initState(); - } + final _appRouter = AppRouter(); @override Widget build(BuildContext context) { - return const MaterialApp( - home: ZxingPage(), + return Observer( + builder: (_) => MaterialApp( + title: 'ZxScanner', + theme: AppTheme.flexLightTheme(), + darkTheme: AppTheme.flexDarkTheme(), + themeMode: appStore.themeMode, + localizationsDelegates: const [ + loc.S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: loc.S.delegate.supportedLocales, + locale: appStore.selectedLanguage.parseLocale(), + onGenerateRoute: _appRouter.onGenerateRoute, + scrollBehavior: MyCustomScrollBehavior(), + debugShowCheckedModeBanner: false, + ), ); } } diff --git a/example/lib/pages/creator_page.dart b/example/lib/pages/creator_page.dart new file mode 100644 index 0000000..7fe6af1 --- /dev/null +++ b/example/lib/pages/creator_page.dart @@ -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 createState() => _CreatorPageState(); +} + +class _CreatorPageState extends State + with TickerProviderStateMixin { + CameraController? controller; + TabController? _tabController; + + bool isAndroid() => Theme.of(context).platform == TargetPlatform.android; + + // Scan result queue + final _resultQueue = []; + + // 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(() {}); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/example/lib/zxing_page.dart b/example/lib/pages/history_page.dart similarity index 96% rename from example/lib/zxing_page.dart rename to example/lib/pages/history_page.dart index 8cc2a67..8621f10 100644 --- a/example/lib/zxing_page.dart +++ b/example/lib/pages/history_page.dart @@ -14,16 +14,16 @@ import 'package:share_plus/share_plus.dart'; late Directory tempDir; String get tempPath => '${tempDir.path}/zxing.jpg'; -class ZxingPage extends StatefulWidget { - const ZxingPage({ +class HistoryPage extends StatefulWidget { + const HistoryPage({ Key? key, }) : super(key: key); @override - State createState() => _ZxingPageState(); + State createState() => _HistoryPageState(); } -class _ZxingPageState extends State with TickerProviderStateMixin { +class _HistoryPageState extends State with TickerProviderStateMixin { CameraController? controller; TabController? _tabController; diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart new file mode 100644 index 0000000..73a3119 --- /dev/null +++ b/example/lib/pages/home_page.dart @@ -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 createState() => _HomePageState(); +} + +class _HomePageState extends State { + 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; + }); + }, + ), + ); + } +} diff --git a/example/lib/pages/scanner_page.dart b/example/lib/pages/scanner_page.dart new file mode 100644 index 0000000..05f0ebb --- /dev/null +++ b/example/lib/pages/scanner_page.dart @@ -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 createState() => _ScannerPageState(); +} + +class _ScannerPageState extends State + 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(), + ], + ), + ); + } +} diff --git a/example/lib/pages/settings_page.dart b/example/lib/pages/settings_page.dart new file mode 100644 index 0000000..6710634 --- /dev/null +++ b/example/lib/pages/settings_page.dart @@ -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 createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { + @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(() {}); + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/example/lib/utils/extensions.dart b/example/lib/utils/extensions.dart new file mode 100644 index 0000000..d5eefb4 --- /dev/null +++ b/example/lib/utils/extensions.dart @@ -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; + } +} diff --git a/example/lib/utils/router.dart b/example/lib/utils/router.dart new file mode 100644 index 0000000..aaed0a6 --- /dev/null +++ b/example/lib/utils/router.dart @@ -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(), + ); + } + } +} diff --git a/example/lib/utils/scroll_behavior.dart b/example/lib/utils/scroll_behavior.dart new file mode 100644 index 0000000..c299410 --- /dev/null +++ b/example/lib/utils/scroll_behavior.dart @@ -0,0 +1,10 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class MyCustomScrollBehavior extends MaterialScrollBehavior { + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }; +} diff --git a/example/lib/widgets/common_widgets.dart b/example/lib/widgets/common_widgets.dart new file mode 100644 index 0000000..3e1c115 --- /dev/null +++ b/example/lib/widgets/common_widgets.dart @@ -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, + ), + ); + } +} diff --git a/example/lib/widgets/language_widget.dart b/example/lib/widgets/language_widget.dart new file mode 100644 index 0000000..755e315 --- /dev/null +++ b/example/lib/widgets/language_widget.dart @@ -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( + 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( + 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(), + ); + } +} diff --git a/example/lib/widgets/setting_tile.dart b/example/lib/widgets/setting_tile.dart new file mode 100644 index 0000000..a7a9f9c --- /dev/null +++ b/example/lib/widgets/setting_tile.dart @@ -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, + ), + ); + } +} diff --git a/example/lib/widgets/theme_mode_switch.dart b/example/lib/widgets/theme_mode_switch.dart new file mode 100644 index 0000000..032e4fc --- /dev/null +++ b/example/lib/widgets/theme_mode_switch.dart @@ -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 onChanged; + + @override + Widget build(BuildContext context) { + final List isSelected = [ + 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 [ + Icon(Icons.wb_sunny), + Icon(Icons.phone_iphone), + Icon(Icons.bedtime), + ], + ); + } +} diff --git a/example/lib/widgets/theme_selector.dart b/example/lib/widgets/theme_selector.dart new file mode 100644 index 0000000..55f7a59 --- /dev/null +++ b/example/lib/widgets/theme_selector.dart @@ -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 { + 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: [ + 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, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index cce4800..a4622ad 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "38.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "3.4.1" archive: dependency: transitive description: @@ -8,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.3.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" async: dependency: transitive description: @@ -22,6 +43,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.10" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.3" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.2.3" camera: dependency: transitive description: @@ -35,7 +112,7 @@ packages: name: camera_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.6" camera_web: dependency: transitive description: @@ -57,6 +134,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" clock: dependency: transitive description: @@ -64,6 +148,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" collection: dependency: transitive description: @@ -71,20 +162,76 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + connectivity_plus_linux: + dependency: transitive + description: + name: connectivity_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + connectivity_plus_macos: + dependency: transitive + description: + name: connectivity_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + connectivity_plus_web: + dependency: transitive + description: + name: connectivity_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + connectivity_plus_windows: + dependency: transitive + description: + name: connectivity_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + convex_bottom_bar: + dependency: "direct main" + description: + name: convex_bottom_bar + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" cross_file: dependency: transitive description: name: cross_file url: "https://pub.dartlang.org" source: hosted - version: "0.3.2" + version: "0.3.3" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" cupertino_icons: dependency: "direct main" description: @@ -92,6 +239,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.3" fake_async: dependency: transitive description: @@ -113,6 +274,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + flex_color_scheme: + dependency: "direct main" + description: + name: flex_color_scheme + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1" flutter: dependency: "direct main" description: flutter @@ -132,6 +307,18 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_mobx: + dependency: "direct main" + description: + name: flutter_mobx + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -156,6 +343,55 @@ packages: relative: true source: path version: "0.0.2" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.9" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "10.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" image: dependency: transitive description: @@ -163,6 +399,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.3" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" js: dependency: transitive description: @@ -170,6 +420,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.0" lints: dependency: transitive description: @@ -177,6 +434,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" matcher: dependency: transitive description: @@ -204,7 +468,42 @@ packages: name: mime url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" + mobx: + dependency: transitive + description: + name: mobx + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + mobx_codegen: + dependency: "direct dev" + description: + name: mobx_codegen + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5+2" + nb_utils: + dependency: "direct main" + description: + name: nb_utils + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.0" + nm: + dependency: transitive + description: + name: nm + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" path: dependency: transitive description: @@ -225,7 +524,7 @@ packages: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.13" path_provider_ios: dependency: transitive description: @@ -282,6 +581,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" process: dependency: transitive description: @@ -289,20 +595,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "3.0.1+1" + version: "3.1.0" share_plus: dependency: "direct main" description: name: share_plus url: "https://pub.dartlang.org" source: hosted - version: "4.0.3" + version: "4.0.4" share_plus_linux: dependency: transitive description: @@ -338,11 +658,88 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" source_span: dependency: transitive description: @@ -392,6 +789,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.8" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" typed_data: dependency: transitive description: @@ -405,14 +809,14 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.20" + version: "6.1.0" url_launcher_android: dependency: transitive description: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.15" + version: "6.0.16" url_launcher_ios: dependency: transitive description: @@ -462,13 +866,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.5.1" + version: "2.5.2" xdg_directories: dependency: transitive description: @@ -483,6 +901,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.3.1" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" sdks: dart: ">=2.16.1 <3.0.0" flutter: ">=2.10.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 0cc1219..7fd9ee6 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -7,18 +7,31 @@ environment: sdk: ">=2.16.1 <3.0.0" dependencies: + convex_bottom_bar: ^3.0.0 cupertino_icons: ^1.0.2 + flex_color_scheme: ^5.0.1 flutter: sdk: flutter + flutter_localizations: + sdk: flutter + flutter_mobx: ^2.0.5 flutter_zxing: path: ../ + font_awesome_flutter: ^10.1.0 + nb_utils: ^4.5.0 path_provider: ^2.0.9 - share_plus: ^4.0.3 + share_plus: ^4.0.4 + +flutter_intl: + main_locale: en_US + enabled: true dev_dependencies: + build_runner: ^2.1.10 flutter_lints: ^1.0.0 flutter_test: sdk: flutter + mobx_codegen: flutter: uses-material-design: true diff --git a/lib/zxing_reader_widget.dart b/lib/zxing_reader_widget.dart index fda0d45..209f9c4 100644 --- a/lib/zxing_reader_widget.dart +++ b/lib/zxing_reader_widget.dart @@ -67,7 +67,9 @@ class _ZxingReaderWidgetState extends State availableCameras().then((cameras) { setState(() { this.cameras = cameras; - onNewCameraSelected(cameras.first); + if (cameras.isNotEmpty) { + onNewCameraSelected(cameras.first); + } }); }); diff --git a/pubspec.yaml b/pubspec.yaml index 623f7e2..2ef4050 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ environment: flutter: ">=2.5.0" dependencies: - camera: ^0.9.4 + camera: 0.9.4+19 ffi: ^1.1.2 flutter: sdk: flutter