diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 05cab0e..68d2893 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -17,12 +17,12 @@
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
-
-
+
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
index 06952be..39a9ca2 100644
--- a/android/app/src/main/res/values-night/styles.xml
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -1,6 +1,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
index cb1ef88..111ba91 100644
--- a/android/app/src/main/res/values/styles.xml
+++ b/android/app/src/main/res/values/styles.xml
@@ -1,6 +1,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 0000000..fa0b357
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/lib/main.dart b/lib/main.dart
index 8f3aac0..add7626 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -4,7 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:google_mlkit_document_scanner/google_mlkit_document_scanner.dart';
import 'package:super_simple_scan/views/document_result_view.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:flex_color_scheme/flex_color_scheme.dart';
+import 'package:dynamic_color/dynamic_color.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
@@ -15,34 +15,38 @@ void main() {
class SuperSimpleScan extends StatelessWidget {
const SuperSimpleScan({super.key});
+ static final _defaultLightColorScheme =
+ ColorScheme.fromSwatch(primarySwatch: Colors.blue);
+
+ static final _defaultDarkColorScheme = ColorScheme.fromSwatch(
+ primarySwatch: Colors.blue, brightness: Brightness.dark);
+
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Super Simple Scan',
- theme: FlexThemeData.light(
- appBarElevation: 0.5,
- useMaterial3: true,
- typography:
- Typography.material2021(platform: TargetPlatform.android)),
- darkTheme: FlexThemeData.dark(
- appBarElevation: 1,
- useMaterial3: true,
- typography:
- Typography.material2021(platform: TargetPlatform.android)),
- themeMode: ThemeMode.system,
- localizationsDelegates: [
- AppLocalizations.delegate,
- GlobalMaterialLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- GlobalCupertinoLocalizations.delegate,
- ],
- supportedLocales: const [
- Locale('en'),
- Locale('de'),
- ],
- home: HomePage(title: "Super Simple Scan"),
- );
+ return DynamicColorBuilder(builder: (lightColorScheme, darkColorScheme) {
+ return MaterialApp(
+ title: 'Super Simple Scan',
+ theme: ThemeData(
+ colorScheme: lightColorScheme ?? _defaultLightColorScheme,
+ ),
+ darkTheme: ThemeData(
+ colorScheme: darkColorScheme ?? _defaultDarkColorScheme,
+ ),
+ themeMode: ThemeMode.system,
+ localizationsDelegates: [
+ AppLocalizations.delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ ],
+ supportedLocales: const [
+ Locale('en'),
+ Locale('de'),
+ ],
+ home: HomePage(title: "Super Simple Scan"),
+ );
+ });
}
}
diff --git a/lib/views/document_result_view.dart b/lib/views/document_result_view.dart
index 328823a..7cac1bb 100644
--- a/lib/views/document_result_view.dart
+++ b/lib/views/document_result_view.dart
@@ -3,9 +3,10 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:flutter_pdfview/flutter_pdfview.dart';
import 'package:google_mlkit_document_scanner/google_mlkit_document_scanner.dart';
import 'package:share_plus/share_plus.dart';
+import 'package:super_simple_scan/widgets/filenameform_widget.dart';
+import 'package:super_simple_scan/widgets/pdfdisplay_widget.dart';
class DocumentResultView extends StatefulWidget {
final DocumentScanningResult document;
@@ -20,25 +21,12 @@ class DocumentResultViewState extends State
with WidgetsBindingObserver {
final GlobalKey _formKey = GlobalKey();
final fileNameFieldController = TextEditingController(text: "");
- int currentPage = 0;
- int? totalPages = 0;
- bool isReady = false;
- String errorMessage = '';
-// Add a pdf suffix to the file name if it is missing
- String _addPdfSuffix(String fileName) {
- if (!fileName.toLowerCase().endsWith('.pdf')) {
- return '$fileName.pdf';
- }
- return fileName;
- }
-
- void setDefaultFileName() {
- DateTime now = DateTime.now();
- fileNameFieldController.text =
- AppLocalizations.of(context)!.defaultFileName(now, now);
- fileNameFieldController.selection = TextSelection(
- baseOffset: 0, extentOffset: fileNameFieldController.text.length);
+ @override
+ void dispose() {
+ // Clean up the controller when the widget is disposed.
+ fileNameFieldController.dispose();
+ super.dispose();
}
@override
@@ -60,131 +48,35 @@ class DocumentResultViewState extends State
Expanded(
child: SizedBox(
height: pdfHeight,
- child: Stack(children: [
- PDFView(
- enableSwipe: true,
- pageFling: true,
- pageSnap: true,
- autoSpacing: true,
- filePath: widget.document.pdf!.uri,
- defaultPage: currentPage,
- swipeHorizontal: true,
- fitPolicy: FitPolicy.BOTH,
- fitEachPage: true,
- preventLinkNavigation: false,
- backgroundColor:
- Theme.of(context).scaffoldBackgroundColor,
- onRender: (pages) {
- setState(() {
- totalPages = pages;
- isReady = true;
- setDefaultFileName();
- });
- },
- onPageChanged: (page, total) {
- setState(() {
- currentPage = page ?? 0;
- totalPages = total;
- });
- },
- onPageError: (page, error) {
- setState(() {
- errorMessage = '$page: ${error.toString()}';
- });
- print('$page: ${error.toString()}');
- },
- ),
- errorMessage.isEmpty
- ? !isReady
- ? Center(
- child: CircularProgressIndicator(),
- )
- : Container()
- : Center(
- child: Text(errorMessage),
- ),
- Positioned(
- bottom: 0,
- width: MediaQuery.of(context).size.width - 32,
- child: Container(
- height: 50,
- alignment: Alignment.center,
- padding: const EdgeInsets.all(8.0),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Theme.of(context)
- .scaffoldBackgroundColor
- .withAlpha(0),
- Theme.of(context).scaffoldBackgroundColor,
- ],
- ),
- ),
- child: Text(
- "${currentPage + 1} / $totalPages",
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
- fontSize: 16,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- )
- ])),
+ child: PdfDisplayWidget(pdfUrl: widget.document.pdf!.uri)),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
),
- Form(
- key: _formKey,
- child: Column(
- children: [
- TextFormField(
- controller: fileNameFieldController,
- autofocus: true,
- decoration: InputDecoration(
- hintText:
- AppLocalizations.of(context)!.enterFileNamePrompt,
- suffixText: '.pdf',
- ),
- validator: (String? value) {
- if (value == null || value.isEmpty) {
- return AppLocalizations.of(context)!.fileNameIsRequired;
- }
- return null;
- },
- onTap: () => fileNameFieldController.selection =
- TextSelection(
- baseOffset: 0,
- extentOffset: fileNameFieldController.text.length),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 16.0),
- child: FilledButton.icon(
- style: ElevatedButton.styleFrom(
- textStyle: TextStyle(fontSize: 20),
- ),
- onPressed: () {
- if (_formKey.currentState!.validate()) {
- _shareDocument(widget.document.pdf!,
- _addPdfSuffix(fileNameFieldController.text));
- }
- },
- label: Text(AppLocalizations.of(context)!.shareButton),
- icon: Icon(Icons.share),
- ),
- ),
- ],
- ),
- ),
+ FileNameFormWidget(
+ formKey: _formKey,
+ fileNameFieldController: fileNameFieldController,
+ onSubmitted: _shareDocument,
+ defaultFileName: _getDefaultFileName()),
],
),
),
);
}
+ // Add a pdf suffix to the file name if it is missing
+ String _addPdfSuffix(String fileName) {
+ if (!fileName.toLowerCase().endsWith('.pdf')) {
+ return '$fileName.pdf';
+ }
+ return fileName;
+ }
+
+ String _getDefaultFileName() {
+ DateTime now = DateTime.now();
+ return AppLocalizations.of(context)!.defaultFileName(now, now);
+ }
+
Future _readFileByte(String filePath) async {
Uri myUri = Uri.parse(filePath);
File audioFile = File.fromUri(myUri);
@@ -198,22 +90,19 @@ class DocumentResultViewState extends State
return bytes;
}
- void _shareDocument(
- DocumentScanningResultPdf document, String fileName) async {
- Uint8List? bytes = await _readFileByte(document.uri);
+ void _shareDocument() async {
+ if (!_formKey.currentState!.validate()) {
+ return;
+ }
+
+ Uint8List? bytes = await _readFileByte(widget.document.pdf!.uri);
if (bytes == null) {
return;
}
+ String fileName = _addPdfSuffix(fileNameFieldController.text);
Share.shareXFiles(
[XFile.fromData(bytes, name: fileName, mimeType: 'application/pdf')],
fileNameOverrides: [fileName],
);
}
-
- @override
- void dispose() {
- // Clean up the controller when the widget is disposed.
- fileNameFieldController.dispose();
- super.dispose();
- }
}
diff --git a/lib/widgets/filenameform_widget.dart b/lib/widgets/filenameform_widget.dart
new file mode 100644
index 0000000..09ec7b3
--- /dev/null
+++ b/lib/widgets/filenameform_widget.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+import 'package:super_simple_scan/widgets/sharebutton_widget.dart';
+import 'package:super_simple_scan/widgets/textfield_widget.dart';
+
+class FileNameFormWidget extends StatelessWidget {
+ final GlobalKey _formKey;
+ final TextEditingController fileNameFieldController;
+ final void Function() onSubmitted;
+ final String defaultFileName;
+
+ FileNameFormWidget(
+ {super.key,
+ required GlobalKey formKey,
+ required this.fileNameFieldController,
+ required this.onSubmitted,
+ required this.defaultFileName})
+ : _formKey = formKey {
+ fileNameFieldController.text = defaultFileName;
+ fileNameFieldController.selection = TextSelection(
+ baseOffset: 0, extentOffset: fileNameFieldController.text.length);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Form(
+ key: _formKey,
+ child: Column(
+ children: [
+ FileNameInputTextField(controller: fileNameFieldController),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16.0),
+ child: ShareButton(
+ onPressed: onSubmitted,
+ ))
+ ],
+ ));
+ }
+}
diff --git a/lib/widgets/pdfdisplay_widget.dart b/lib/widgets/pdfdisplay_widget.dart
new file mode 100644
index 0000000..cd8ccf0
--- /dev/null
+++ b/lib/widgets/pdfdisplay_widget.dart
@@ -0,0 +1,91 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_pdfview/flutter_pdfview.dart';
+
+class PdfDisplayWidget extends StatefulWidget {
+ final String pdfUrl;
+
+ const PdfDisplayWidget({super.key, required this.pdfUrl});
+
+ @override
+ PdfDisplayWidgetState createState() => PdfDisplayWidgetState();
+}
+
+class PdfDisplayWidgetState extends State {
+ int currentPage = 0;
+ int? totalPages = 0;
+ bool isReady = false;
+ String errorMessage = '';
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(children: [
+ PDFView(
+ enableSwipe: true,
+ pageFling: true,
+ pageSnap: true,
+ autoSpacing: true,
+ filePath: widget.pdfUrl,
+ defaultPage: currentPage,
+ swipeHorizontal: true,
+ fitPolicy: FitPolicy.BOTH,
+ fitEachPage: true,
+ preventLinkNavigation: false,
+ backgroundColor: Theme.of(context).scaffoldBackgroundColor,
+ onRender: (pages) {
+ setState(() {
+ totalPages = pages;
+ isReady = true;
+ });
+ },
+ onPageChanged: (page, total) {
+ setState(() {
+ currentPage = page ?? 0;
+ totalPages = total;
+ });
+ },
+ onPageError: (page, error) {
+ setState(() {
+ errorMessage = '$page: ${error.toString()}';
+ });
+ print('$page: ${error.toString()}');
+ },
+ ),
+ errorMessage.isEmpty
+ ? !isReady
+ ? Center(
+ child: CircularProgressIndicator(),
+ )
+ : Container()
+ : Center(
+ child: Text(errorMessage),
+ ),
+ Positioned(
+ bottom: 0,
+ width: MediaQuery.of(context).size.width - 32,
+ child: Container(
+ height: 50,
+ alignment: Alignment.center,
+ padding: const EdgeInsets.all(8.0),
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Theme.of(context).scaffoldBackgroundColor.withAlpha(0),
+ Theme.of(context).scaffoldBackgroundColor,
+ ],
+ ),
+ ),
+ child: Text(
+ "${currentPage + 1} / $totalPages",
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ )
+ ]);
+ }
+}
diff --git a/lib/widgets/sharebutton_widget.dart b/lib/widgets/sharebutton_widget.dart
new file mode 100644
index 0000000..510b1cc
--- /dev/null
+++ b/lib/widgets/sharebutton_widget.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+class ShareButton extends StatelessWidget {
+ final void Function() onPressed;
+
+ const ShareButton({super.key, required this.onPressed});
+
+ @override
+ Widget build(BuildContext context) {
+ return FilledButton.icon(
+ style: ElevatedButton.styleFrom(
+ textStyle: TextStyle(fontSize: 20),
+ ),
+ onPressed: onPressed,
+ label: Text(AppLocalizations.of(context)!.shareButton),
+ icon: Icon(Icons.share),
+ );
+ }
+}
diff --git a/lib/widgets/textfield_widget.dart b/lib/widgets/textfield_widget.dart
new file mode 100644
index 0000000..e983905
--- /dev/null
+++ b/lib/widgets/textfield_widget.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+class FileNameInputTextField extends StatelessWidget {
+ final TextEditingController controller;
+ final String? Function(String?)? validator;
+ final void Function(String)? onChanged;
+
+ const FileNameInputTextField({
+ super.key,
+ required this.controller,
+ this.validator,
+ this.onChanged,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return TextFormField(
+ autofocus: true,
+ controller: controller,
+ decoration: InputDecoration(
+ labelText: AppLocalizations.of(context)!.enterFileNamePrompt,
+ suffixText: '.pdf',
+ ),
+ validator: (String? value) {
+ if (value == null || value.isEmpty) {
+ return AppLocalizations.of(context)!.fileNameIsRequired;
+ }
+ return null;
+ },
+ onChanged: onChanged,
+ onTap: () => controller.selection =
+ TextSelection(baseOffset: 0, extentOffset: controller.text.length),
+ );
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index 153d863..b7789e5 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -97,6 +97,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
+ dynamic_color:
+ dependency: "direct main"
+ description:
+ name: dynamic_color
+ sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.7.0"
fake_async:
dependency: transitive
description:
@@ -129,22 +137,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
- flex_color_scheme:
- dependency: "direct main"
- description:
- name: flex_color_scheme
- sha256: "90f4fe67b9561ae8a4af117df65a8ce9988624025667c54e6d304e65cff77d52"
- url: "https://pub.dev"
- source: hosted
- version: "8.0.2"
- flex_seed_scheme:
- dependency: transitive
- description:
- name: flex_seed_scheme
- sha256: "7639d2c86268eff84a909026eb169f008064af0fb3696a651b24b0fa24a40334"
- url: "https://pub.dev"
- source: hosted
- version: "3.4.1"
flutter:
dependency: "direct main"
description: flutter
diff --git a/pubspec.yaml b/pubspec.yaml
index 5f6e53a..44687d1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -42,7 +42,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
share_plus: ^10.1.3
- flex_color_scheme: ^8.0.2
+ dynamic_color: ^1.7.0
dev_dependencies:
flutter_test: