diff --git a/.metadata b/.metadata index 6ac2303..56bfc2c 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe + revision: f4abaa0735eba4dfd8f33f73363911d63931fe03 channel: stable project_type: app diff --git a/README.md b/README.md index 8d57857..cc8b706 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # fcs +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/android/.gitignore b/android/.gitignore index bc2100d..0a741cb 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -5,3 +5,7 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/android/.project b/android/.project deleted file mode 100644 index 3964dd3..0000000 --- a/android/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - android - Project android created by Buildship. - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.buildship.core.gradleprojectnature - - diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index d19d611..0000000 --- a/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments= -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home=/usr/lib/jvm/java-8-openjdk-amd64 -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/android/app/.classpath b/android/app/.classpath deleted file mode 100644 index eb19361..0000000 --- a/android/app/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/android/app/.project b/android/app/.project deleted file mode 100644 index ac485d7..0000000 --- a/android/app/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - app - Project app created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - diff --git a/android/app/.settings/org.eclipse.buildship.core.prefs b/android/app/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index b1886ad..0000000 --- a/android/app/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -connection.project.dir=.. -eclipse.preferences.version=1 diff --git a/android/app/build.gradle b/android/app/build.gradle index 5492e81..0e9c8c7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,26 +24,17 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" -def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - android { - compileSdkVersion 28 - - lintOptions { - disable 'InvalidPackage' - } + compileSdkVersion 30 defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.mokkon.fcs.fcs" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName multiDexEnabled true - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -92,17 +83,8 @@ flutter { source '../..' } -apply plugin: 'kotlin-android' - dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - implementation 'com.android.support:multidex:1.0.3' - implementation 'com.google.firebase:firebase-analytics:17.5.0' - implementation 'com.google.firebase:firebase-auth:19.0.0' - implementation 'com.google.firebase:firebase-messaging:20.1.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.android.support:multidex:1.0.3' } apply plugin: 'com.google.gms.google-services' diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 64c388e..e5bd26c 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.mokkon.fcs.fcs"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e32dd3b..890efd6 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,43 +1,41 @@ - - - - - - - - + package="com.mokkon.fcs.fcs"> + - + + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> + + - - - - + + diff --git a/android/app/src/main/java/com/mokkon/fcs/fcs/MainActivity.java b/android/app/src/main/java/com/mokkon/fcs/fcs/MainActivity.java new file mode 100644 index 0000000..2069e10 --- /dev/null +++ b/android/app/src/main/java/com/mokkon/fcs/fcs/MainActivity.java @@ -0,0 +1,6 @@ +package com.mokkon.fcs.fcs; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/android/app/src/main/java/com/mokkon/fcs_dev/fcs/Application.java b/android/app/src/main/java/com/mokkon/fcs_dev/fcs/Application.java deleted file mode 100644 index 1fe18f1..0000000 --- a/android/app/src/main/java/com/mokkon/fcs_dev/fcs/Application.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mokkon.fcs_dev.fcs; - - import io.flutter.app.FlutterApplication; - import io.flutter.plugin.common.PluginRegistry; - import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback; - import io.flutter.plugins.GeneratedPluginRegistrant; - import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService; - - public class Application extends FlutterApplication implements PluginRegistrantCallback { - @Override - public void onCreate() { - super.onCreate(); - FlutterFirebaseMessagingService.setPluginRegistrant(this); - } - - @Override - public void registerWith(PluginRegistry registry) { - GeneratedPluginRegistrant.registerWith(registry); - } - } diff --git a/android/app/src/main/java/com/mokkon/fcs_dev/fcs/MainActivity.java b/android/app/src/main/java/com/mokkon/fcs_dev/fcs/MainActivity.java deleted file mode 100644 index 4a8f859..0000000 --- a/android/app/src/main/java/com/mokkon/fcs_dev/fcs/MainActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mokkon.fcs_dev.fcs; - -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index b1da307..db77bb4 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index cdf697f..17987b7 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index a8316e6..09d4391 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 2b85987..d5f1c8d 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 7e0c447..4d6372e 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 0a46b50..0000000 --- a/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - FCS - diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 1f83a33..d74aa35 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - - diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index 64c388e..e5bd26c 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.mokkon.fcs.fcs"> diff --git a/android/build.gradle b/android/build.gradle index b2c3f03..9070705 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,15 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.61' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' - classpath 'com.google.gms:google-services:4.3.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12' //add this line + classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.google.gms:google-services:4.3.8' } } @@ -23,8 +20,6 @@ allprojects { rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { project.evaluationDependsOn(':app') } diff --git a/android/gradle.properties b/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146..bc6a58a 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro deleted file mode 100644 index 70885bd..0000000 --- a/android/proguard-rules.pro +++ /dev/null @@ -1,7 +0,0 @@ -## Flutter wrapper --keep class io.flutter.app.** { *; } --keep class io.flutter.plugin.** { *; } --keep class io.flutter.util.** { *; } --keep class io.flutter.view.** { *; } --keep class io.flutter.** { *; } --keep class io.flutter.plugins.** { *; } \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 5a2f14f..44e62bc 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle deleted file mode 100644 index e7b4def..0000000 --- a/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/lib/data/provider/messaging_fcm.dart b/lib/data/provider/messaging_fcm.dart index a184004..c008ecd 100644 --- a/lib/data/provider/messaging_fcm.dart +++ b/lib/data/provider/messaging_fcm.dart @@ -1,57 +1,53 @@ -import 'dart:io'; - import 'package:fcs/data/services/messaging_service.dart'; +import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:logging/logging.dart'; final msgLog = Logger('backgroundMessageHandler'); -Future backgroundMessageHandler(Map message) async { - if (message.containsKey('data')) { - // Handle data message - final dynamic data = message['data']; - msgLog.info("background onMessage: $message"); - } - - if (message.containsKey('notification')) { - // Handle notification message - final dynamic notification = message['notification']; - } +Future backgroundMessageHandler(RemoteMessage message) async { + await Firebase.initializeApp(); + msgLog.info("background onMessage: $message"); } class MessagingFCM { final log = Logger('MessagingFCM'); - FirebaseMessaging _firebaseMessaging; + late FirebaseMessaging _firebaseMessaging; MessagingFCM(OnNotify onMessage, - {OnNotify onLaunch, OnNotify onResume, OnSetupComplete onSetupComplete}) { - _firebaseMessaging = FirebaseMessaging(); - _firebaseMessaging.configure( - onMessage: (Map message) async { - log.info("onMessage: $message"); - if (onMessage != null) _onNotify(message, onMessage); - }, - onBackgroundMessage: Platform.isIOS ? null : backgroundMessageHandler, - onLaunch: (Map message) async { - log.info("onLaunch: $message"); - if (onLaunch != null) _onNotify(message, onLaunch); - }, - onResume: (Map message) async { - log.info("onResume: $message"); - if (onResume != null) _onNotify(message, onResume); - }, + {OnNotify? onLaunch, + OnNotify? onResume, + OnSetupComplete? onSetupComplete}) { + _firebaseMessaging = FirebaseMessaging.instance; + init(onMessage: onMessage, onSetupComplete: onSetupComplete); + } + + init({OnNotify? onMessage, OnSetupComplete? onSetupComplete}) async { + NotificationSettings settings = await _firebaseMessaging.requestPermission( + alert: true, + announcement: false, + badge: true, + carPlay: false, + criticalAlert: false, + provisional: false, + sound: true, ); - _firebaseMessaging - .requestNotificationPermissions(const IosNotificationSettings()); - _firebaseMessaging.onIosSettingsRegistered - .listen((IosNotificationSettings settings) { - log.info("Settings registered: $settings"); - }); - _firebaseMessaging.getToken().then((String token) { - if (onSetupComplete != null) onSetupComplete(token); - log.info("Messaging Token:$token"); + + print('User granted permission: ${settings.authorizationStatus}'); + + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + log.info("onMessage: $message"); + if (onMessage != null) _onNotify(message.data, onMessage); + + if (message.notification != null) { + print('Message also contained a notification: ${message.notification}'); + } }); + + String? token = await _firebaseMessaging.getToken(); + if (onSetupComplete != null && token != null) onSetupComplete(token); + log.info("Messaging Token:$token"); } Future subscribeToTopic(String topic) { diff --git a/lib/data/services/messaging_imp.dart b/lib/data/services/messaging_imp.dart index ceb6e57..9b41260 100644 --- a/lib/data/services/messaging_imp.dart +++ b/lib/data/services/messaging_imp.dart @@ -5,11 +5,13 @@ import 'messaging_service.dart'; class MessagingServiceImp implements MessagingService { MessagingServiceImp(); - static MessagingFCM messagingFCM; + late MessagingFCM messagingFCM; @override void init(onMessage, - {OnNotify onLaunch, OnNotify onResume, OnSetupComplete onSetupComplete}) { + {OnNotify? onLaunch, + OnNotify? onResume, + OnSetupComplete? onSetupComplete}) { messagingFCM = MessagingFCM(onMessage, onLaunch: onLaunch, onResume: onResume, diff --git a/lib/domain/entities/invoice.dart b/lib/domain/entities/invoice.dart index 3ac1c9e..a15e905 100644 --- a/lib/domain/entities/invoice.dart +++ b/lib/domain/entities/invoice.dart @@ -28,7 +28,7 @@ class Invoice { List customDuties; List cartons; List cargoTypes; - List shipments; + List? shipments; List payments; Discount? discount; PaymentMethod? paymentMethod; @@ -85,9 +85,9 @@ class Invoice { } double getHandlingFee() { - return shipments - .where((sh) => sh.isSelected) - .fold(0, (p, s) => p + (s.handlingFee - s.paidHandlingFee)); + return shipments! + .where((sh) => sh!.isSelected) + .fold(0, (p, s) => p + (s!.handlingFee - s.paidHandlingFee)); } double getTotalBalance(Rate rate) { @@ -176,7 +176,7 @@ class Invoice { List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList(); List _customDuties = customDuties.map((c) => c.toMap()).toList(); List _cartons = cartons.map((c) => c.toMap()).toList(); - List _shipments = shipments.map((s) => s.toMap()).toList(); + List _shipments = shipments!.map((s) => s!.toMap()).toList(); return { "id": id, "invoice_date": invoiceDate?.toUtc().toIso8601String(), diff --git a/lib/domain/entities/package.dart b/lib/domain/entities/package.dart index c583de1..2df0c7d 100644 --- a/lib/domain/entities/package.dart +++ b/lib/domain/entities/package.dart @@ -36,7 +36,7 @@ class Package { DeliveryAddress? deliveryAddress; //for packages in processing - List photoFiles; + List photoFiles; int get amount => rate != null && weight != null ? rate * weight : 0; diff --git a/lib/helpers/firebase_helper.dart b/lib/helpers/firebase_helper.dart index 0fd5b84..4014aa8 100644 --- a/lib/helpers/firebase_helper.dart +++ b/lib/helpers/firebase_helper.dart @@ -24,20 +24,24 @@ Future getClaims({bool refreshIdToken = false}) async { } // returns list of url -Future> uploadFiles(String path, List files, +Future> uploadFiles(String path, List files, {String? fileName}) async { List> fu = []; - for (File f in files) { + for (File? f in files) { Future u = uploadStorage(path, f); fu.add(u); } return Future.wait(fu); } -Future uploadStorage(String path, File file, {String? fileName}) async { +Future uploadStorage(String path, File? file, + {String? fileName}) async { if (fileName == null) { fileName = Uuid().v4(); } + if (file == null) { + return Future.value(''); + } Reference ref = FirebaseStorage.instance.ref().child('$path/$fileName'); UploadTask uploadTask = ref.putFile(file); await uploadTask.resume(); @@ -57,10 +61,11 @@ Future uploadStorage(String path, File file, {String? fileName}) async { // return downloadUrl; } -Future deleteStorageFromUrls(List urls) async { +Future deleteStorageFromUrls(List urls) async { if (urls == null) return; for (int i = 0; i < urls.length; i++) { - await deleteStorageFromUrl(urls[i]); + if (urls[i] == null) return; + await deleteStorageFromUrl(urls[i]!); } } diff --git a/lib/main-dev.dart b/lib/main-dev.dart index 7ad6513..8964b01 100644 --- a/lib/main-dev.dart +++ b/lib/main-dev.dart @@ -1,11 +1,15 @@ +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'app.dart'; import 'config.dart'; +import 'data/provider/messaging_fcm.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); + FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); + Config( flavor: Flavor.DEV, color: Colors.blue, diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..b9bfc38 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/lib/pages/carton/cargo_type_editor.dart b/lib/pages/carton/cargo_type_editor.dart index 7c8553a..1d6572c 100644 --- a/lib/pages/carton/cargo_type_editor.dart +++ b/lib/pages/carton/cargo_type_editor.dart @@ -39,7 +39,7 @@ class _CargoTypeEditorState extends State { _loadDefalut() { ShipmentRateModel shipmentRateModel = Provider.of(context, listen: false); - _cargo = shipmentRateModel.rate.defaultCargoType?.clone(); + _cargo = shipmentRateModel.rate.defaultCargoType.clone(); } @override diff --git a/lib/pages/carton_search/carton_search.dart b/lib/pages/carton_search/carton_search.dart index 5442616..07c2215 100644 --- a/lib/pages/carton_search/carton_search.dart +++ b/lib/pages/carton_search/carton_search.dart @@ -171,7 +171,7 @@ class PartSearchDelegate extends SearchDelegate { // } try { - String barcode = await scanBarcode(); + String? barcode = await scanBarcode(); if (barcode != null) { query = barcode; showResults(context); diff --git a/lib/pages/invoice/editor/invoice_editor.dart b/lib/pages/invoice/editor/invoice_editor.dart index c8b9efa..99b5143 100644 --- a/lib/pages/invoice/editor/invoice_editor.dart +++ b/lib/pages/invoice/editor/invoice_editor.dart @@ -103,10 +103,10 @@ class _InvoiceEditorState extends State { _loadShipments() async { ShipmentModel shipmentModel = Provider.of(context, listen: false); - List shipments = await shipmentModel.getShipmentWithHandlingFee( + List? shipments = await shipmentModel.getShipmentWithHandlingFee( widget.fcsShipment!.id!, widget.customer!.id!); - shipments.forEach((s) { - s.isSelected = true; + shipments!.forEach((s) { + s!.isSelected = true; }); setState(() { _invoice!.shipments = shipments; @@ -380,8 +380,8 @@ class _InvoiceEditorState extends State { if (shipment == null) return; shipment.isSelected = true; setState(() { - _invoice!.shipments.remove(shipment); - _invoice!.shipments.add(shipment); + _invoice!.shipments!.remove(shipment); + _invoice!.shipments!.add(shipment); }); } @@ -389,8 +389,8 @@ class _InvoiceEditorState extends State { if (shipment == null) return; shipment.isSelected = false; setState(() { - _invoice!.shipments.remove(shipment); - _invoice!.shipments.add(shipment); + _invoice!.shipments!.remove(shipment); + _invoice!.shipments!.add(shipment); }); } @@ -431,7 +431,7 @@ class _InvoiceEditorState extends State { invoice.handlingFee = _invoice!.getHandlingFee(); invoice.cartons = _invoice!.cartons.where((c) => c.isChecked!).toList(); invoice.shipments = - _invoice!.shipments.where((s) => s.isSelected).toList(); + _invoice!.shipments!.where((s) => s!.isSelected).toList(); invoice.discount = _invoice!.discount; invoice.deliveryFee = _invoice!.deliveryFee; diff --git a/lib/pages/invoice/editor/invoice_handling_fee_list.dart b/lib/pages/invoice/editor/invoice_handling_fee_list.dart index 5ee937d..75d6462 100644 --- a/lib/pages/invoice/editor/invoice_handling_fee_list.dart +++ b/lib/pages/invoice/editor/invoice_handling_fee_list.dart @@ -9,7 +9,7 @@ typedef OnAdd(Shipment shipment); typedef OnRemove(Shipment shipment); class InvoiceHandlingFeeList extends StatelessWidget { - final List? shipments; + final List? shipments; final OnAdd? onAdd; final OnRemove? onRemove; @@ -72,7 +72,7 @@ class InvoiceHandlingFeeList extends StatelessWidget { onSelectChanged: (value) => Navigator.pop(context, c), cells: [ MyDataCell(new Text( - c.shipmentNumber!, + c!.shipmentNumber!, style: textStyle, )), MyDataCell( diff --git a/lib/pages/invoice/invoice_info.dart b/lib/pages/invoice/invoice_info.dart index ba3adbc..d9980ec 100644 --- a/lib/pages/invoice/invoice_info.dart +++ b/lib/pages/invoice/invoice_info.dart @@ -39,8 +39,8 @@ class _InvoiceInfoState extends State { void initState() { super.initState(); _invoice = widget.invoice!; - _invoice!.shipments.forEach((s) { - s.isSelected = true; + _invoice!.shipments!.forEach((s) { + s!.isSelected = true; }); _loadCartons(); } diff --git a/lib/pages/invoice/invoice_table.dart b/lib/pages/invoice/invoice_table.dart index 46ed34b..77812dd 100644 --- a/lib/pages/invoice/invoice_table.dart +++ b/lib/pages/invoice/invoice_table.dart @@ -70,11 +70,11 @@ class InvoiceTable extends StatelessWidget { "${c.calWeight.toStringAsFixed(2)} x ${c.calRate.toStringAsFixed(2)}", amount: "${c.calAmount.toStringAsFixed(2)}")); }); - invoice!.shipments.where((ss) => (ss.isSelected )).forEach((s) { + invoice!.shipments!.where((ss) => (ss!.isSelected )).forEach((s) { tableRows.add(InvoiceTableRow( data: s, invoiceDataType: InvoiceDataType.HandlingFeeType, - desc: "Handling fee\n${s.shipmentNumber}", + desc: "Handling fee\n${s!.shipmentNumber}", rate: "", amount: "${s.handlingFee.toStringAsFixed(2)}")); }); diff --git a/lib/pages/package/model/package_model.dart b/lib/pages/package/model/package_model.dart index 9e979e1..380f9b7 100644 --- a/lib/pages/package/model/package_model.dart +++ b/lib/pages/package/model/package_model.dart @@ -260,7 +260,7 @@ class PackageModel extends BaseModel { } Future createReceiving( - User user, Package package, List files) async { + User user, Package package, List files) async { if (user != null) { package.fcsID = user.fcsID; } @@ -288,22 +288,21 @@ class PackageModel extends BaseModel { } } - Future updateReceiving(User user, Package package, List files, - List deletedUrls) async { + Future updateReceiving(User user, Package package, List files, + List deletedUrls) async { if (user != null) { package.fcsID = user.fcsID; } if (deletedUrls != null) { - for (String url in deletedUrls) { + for (String? url in deletedUrls) { package.photoUrls.remove(url); } } List uploadedURL = []; if (files != null) { - var count = (package.photoUrls?.length ?? 0) + - files.length - - (deletedUrls?.length ?? 0); + var count = + (package.photoUrls.length) + files.length - (deletedUrls.length); if (count > uploadPhotoLimit) throw Exception("Exceed number of file upload"); @@ -333,18 +332,17 @@ class PackageModel extends BaseModel { } Future updateProcessing( - Package package, List files, List deletedUrls) async { + Package package, List files, List deletedUrls) async { if (deletedUrls != null) { - for (String url in deletedUrls) { + for (String? url in deletedUrls) { package.photoUrls.remove(url); } } List uploadedURL = []; if (files != null) { - var count = (package.photoUrls?.length ?? 0) + - files.length - - (deletedUrls?.length ?? 0); + var count = + (package.photoUrls.length) + files.length - (deletedUrls.length); if (count > uploadPhotoLimit) throw Exception("Exceed number of file upload"); diff --git a/lib/pages/package/tracking_id_page.dart b/lib/pages/package/tracking_id_page.dart index 1856f7a..6cdd773 100644 --- a/lib/pages/package/tracking_id_page.dart +++ b/lib/pages/package/tracking_id_page.dart @@ -164,7 +164,7 @@ class _TrackingIDPageState extends State { // } try { - String barcode = await scanBarcode(); + String? barcode = await scanBarcode(); if (barcode != null) { setState(() { _transcationIDCtl.text = barcode; diff --git a/lib/pages/package_search/package_serach.dart b/lib/pages/package_search/package_serach.dart index 18b57ef..566b46c 100644 --- a/lib/pages/package_search/package_serach.dart +++ b/lib/pages/package_search/package_serach.dart @@ -147,7 +147,7 @@ class PackageSearchDelegate extends SearchDelegate { // Barcode bc = barcodes.firstWhere((element) => true); // String barcode; // if (bc != null) barcode = bc.rawValue; - String barcode = await scanBarcode(); + String? barcode = await scanBarcode(); if (barcode != null) { query = barcode; showResults(context); diff --git a/lib/pages/profile/profile_page.dart b/lib/pages/profile/profile_page.dart index a618c3a..3732104 100644 --- a/lib/pages/profile/profile_page.dart +++ b/lib/pages/profile/profile_page.dart @@ -80,7 +80,7 @@ class _ProfileState extends State { ); final phonenumberbox = DisplayText( - text: mainModel.user!.phone ?? "", + text: mainModel.user!.phone, labelTextKey: "profile.phone", iconData: Icons.phone, ); diff --git a/lib/pages/rates/custom_editor.dart b/lib/pages/rates/custom_editor.dart index 4e7f2b1..bec5ddb 100644 --- a/lib/pages/rates/custom_editor.dart +++ b/lib/pages/rates/custom_editor.dart @@ -36,7 +36,7 @@ class _CustomEditorState extends State { _productController.text = _custom.name??""; _feeController.text = _custom.customDutyFee.toStringAsFixed(2); _shipmentRateController.text = - _custom.rate == null ? "" : _custom.rate?.toStringAsFixed(2) ?? ''; + _custom.rate == null ? "" : _custom.rate.toStringAsFixed(2); } else { _isNew = true; } diff --git a/lib/pages/rates/custom_list.dart b/lib/pages/rates/custom_list.dart index 898c8a3..f5a1dc9 100644 --- a/lib/pages/rates/custom_list.dart +++ b/lib/pages/rates/custom_list.dart @@ -86,7 +86,7 @@ class _CustomListState extends State { custom.rate == null ? "" : "Shipment rate \$ " + - custom.rate!.toStringAsFixed(2)), + custom.rate.toStringAsFixed(2)), ), ); }), diff --git a/lib/pages/rates/shipment_rates_calculate.dart b/lib/pages/rates/shipment_rates_calculate.dart index d956a55..c722c86 100644 --- a/lib/pages/rates/shipment_rates_calculate.dart +++ b/lib/pages/rates/shipment_rates_calculate.dart @@ -62,11 +62,11 @@ class _ShipmentRatesCalState extends State { var amount = box.calAmount(rate); var shipmentWeight = box.getShipmentWeight(rate.volumetricRatio); var effectiveWeight = - _cargoType.weight! > shipmentWeight ? _cargoType.weight : shipmentWeight; + _cargoType.weight > shipmentWeight ? _cargoType.weight : shipmentWeight; setState(() { _deliveryFee = - effectiveWeight! > rate.freeDeliveryWeight ? 0 : rate.deliveryFee; + effectiveWeight > rate.freeDeliveryWeight ? 0 : rate.deliveryFee; _amount = amount == null ? 0 : amount + _deliveryFee; _shipmentWeight = shipmentWeight.toDouble(); }); diff --git a/lib/pages/receiving/receiving_editor.dart b/lib/pages/receiving/receiving_editor.dart index e8ec46c..a120ee0 100644 --- a/lib/pages/receiving/receiving_editor.dart +++ b/lib/pages/receiving/receiving_editor.dart @@ -212,7 +212,7 @@ class _ReceivingEditorState extends State { // } try { - String barcode = await scanBarcode(); + String? barcode = await scanBarcode(); if (barcode != null) { setState(() { _trackingIDCtl.text = barcode; diff --git a/lib/pages/shipment/shipment_assign.dart b/lib/pages/shipment/shipment_assign.dart index 7c37695..ad2e808 100644 --- a/lib/pages/shipment/shipment_assign.dart +++ b/lib/pages/shipment/shipment_assign.dart @@ -56,7 +56,7 @@ class _ShipmentAssignState extends State { _selectedShipmentType = _shipment!.shipmentType; _fromTimeEditingController.text = _shipment!.pickupTimeStart!; _toTimeEditingController.text = _shipment!.pickupTimeEnd!; - _pickupDate.text = dateFormatter.format(_shipment!.pickupDate! ?? now); + _pickupDate.text = dateFormatter.format(_shipment!.pickupDate ?? now); _handlingFee.text = _shipment!.handlingFee != null ? _shipment!.handlingFee.toString() : "0"; diff --git a/lib/pages/shipment/shipment_confirm.dart b/lib/pages/shipment/shipment_confirm.dart index 68daf42..05ed394 100644 --- a/lib/pages/shipment/shipment_confirm.dart +++ b/lib/pages/shipment/shipment_confirm.dart @@ -37,7 +37,7 @@ class _ShipmentConfirmState extends State { super.initState(); _shipment = widget.shipment; - _handlingFee.text = _shipment!.handlingFee?.toString() ?? "0"; + _handlingFee.text = _shipment!.handlingFee.toString(); } @override diff --git a/lib/pages/shipment/shipment_info.dart b/lib/pages/shipment/shipment_info.dart index 382d2af..ee29ad5 100644 --- a/lib/pages/shipment/shipment_info.dart +++ b/lib/pages/shipment/shipment_info.dart @@ -223,7 +223,7 @@ class _ShipmentInfoState extends State { iconData: MaterialCommunityIcons.worker); var handlingFeeBox = DisplayText( labelTextKey: "shipment.handling.fee", - text: (_shipment!.handlingFee ?? 0).toString(), + text: (_shipment!.handlingFee).toString(), iconData: FontAwesome.truck); final assignCompleteBtn = LocalButton( diff --git a/lib/pages/term/term_edit.dart b/lib/pages/term/term_edit.dart index 2a5c188..48d11e7 100644 --- a/lib/pages/term/term_edit.dart +++ b/lib/pages/term/term_edit.dart @@ -8,27 +8,28 @@ import 'package:fcs/pages/widgets/local_text.dart'; import 'package:fcs/pages/widgets/progress.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_icons_null_safety/flutter_icons_null_safety.dart'; import 'package:provider/provider.dart'; -import 'package:zefyr/zefyr.dart'; +// import 'package:zefyr/zefyr.dart'; typedef void ProfileCallback(); class TermEdit extends StatefulWidget { final Term term; - TermEdit({this.term}); + TermEdit({required this.term}); @override _TermEditState createState() => _TermEditState(); } class _TermEditState extends State { /// Allows to control the editor and the document. - ZefyrController _controllerEng; - ZefyrController _controllerMm; + // ZefyrController _controllerEng; + // ZefyrController _controllerMm; /// Zefyr editor like any other input field requires a focus node. - FocusNode _focusNodeEng; - FocusNode _focusNodeMm; - bool _isLoading; + // FocusNode _focusNodeEng; + // FocusNode _focusNodeMm; + bool _isLoading = false; @override void initState() { @@ -36,23 +37,23 @@ class _TermEditState extends State { _isLoading = false; // Here we must load the document and pass it to Zefyr controller. - _controllerEng = ZefyrController(_loadDocument(widget.term.termEng)); - _controllerMm = ZefyrController(_loadDocument(widget.term.termMm)); - _focusNodeEng = FocusNode(); - _focusNodeMm = FocusNode(); + // _controllerEng = ZefyrController(_loadDocument(widget.term.termEng)); + // _controllerMm = ZefyrController(_loadDocument(widget.term.termMm)); + // _focusNodeEng = FocusNode(); + // _focusNodeMm = FocusNode(); } /// Loads the document to be edited in Zefyr. - NotusDocument _loadDocument(String data) { - NotusDocument doc; - try { - doc = NotusDocument.fromJson(jsonDecode(data)); - } catch (e) {} - if (doc == null) { - doc = NotusDocument(); - } - return doc; - } + // NotusDocument _loadDocument(String data) { + // NotusDocument doc; + // try { + // doc = NotusDocument.fromJson(jsonDecode(data)); + // } catch (e) {} + // if (doc == null) { + // doc = NotusDocument(); + // } + // return doc; + // } @override Widget build(BuildContext context) { @@ -115,8 +116,8 @@ class _TermEditState extends State { height: MediaQuery.of(context).size.height - 200, child: TabBarView( children: [ - textEditor(_controllerEng, _focusNodeEng), - textEditor(_controllerMm, _focusNodeMm), + // textEditor(_controllerEng, _focusNodeEng), + // textEditor(_controllerMm, _focusNodeMm), ], ), ), @@ -128,35 +129,35 @@ class _TermEditState extends State { ); } - Widget textEditor(ZefyrController controller, FocusNode focusNode) { - return ListView( - children: [ - Container( - height: MediaQuery.of(context).size.height - 200, - child: ZefyrScaffold( - child: ZefyrTheme( - data: ZefyrThemeData().copyWith( - defaultLineTheme: LineTheme( - padding: EdgeInsets.all(0), - textStyle: TextStyle(fontFamily: "Myanmar3"), - ), - ), - child: ZefyrEditor( - autofocus: false, - padding: EdgeInsets.all(16), - controller: controller, - focusNode: focusNode, - ), - ), - ), - ), - // savebtn, - SizedBox( - height: 10, - ) - ], - ); - } + // Widget textEditor(ZefyrController controller, FocusNode focusNode) { + // return ListView( + // children: [ + // Container( + // height: MediaQuery.of(context).size.height - 200, + // child: ZefyrScaffold( + // child: ZefyrTheme( + // data: ZefyrThemeData().copyWith( + // defaultLineTheme: LineTheme( + // padding: EdgeInsets.all(0), + // textStyle: TextStyle(fontFamily: "Myanmar3"), + // ), + // ), + // child: ZefyrEditor( + // autofocus: false, + // padding: EdgeInsets.all(16), + // controller: controller, + // focusNode: focusNode, + // ), + // ), + // ), + // ), + // // savebtn, + // SizedBox( + // height: 10, + // ) + // ], + // ); + // } _unfocus() { FocusScope.of(context).unfocus(); @@ -167,11 +168,11 @@ class _TermEditState extends State { _isLoading = true; }); try { - final contentsEng = jsonEncode(_controllerEng.document); - final contentsMm = jsonEncode(_controllerMm.document); - print('contents => $contentsEng'); - TermModel termModel = Provider.of(context, listen: false); - await termModel.saveTerm(Term(termEng: contentsEng, termMm: contentsMm)); + // final contentsEng = jsonEncode(_controllerEng.document); + // final contentsMm = jsonEncode(_controllerMm.document); + // print('contents => $contentsEng'); + // TermModel termModel = Provider.of(context, listen: false); + // await termModel.saveTerm(Term(termEng: contentsEng, termMm: contentsMm)); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { diff --git a/lib/pages/term/term_page.dart b/lib/pages/term/term_page.dart index b8913b3..ee765c8 100644 --- a/lib/pages/term/term_page.dart +++ b/lib/pages/term/term_page.dart @@ -10,46 +10,46 @@ import 'package:fcs/pages/widgets/local_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:zefyr/zefyr.dart'; +// import 'package:zefyr/zefyr.dart'; typedef void ProfileCallback(); class TermPage extends StatefulWidget { const TermPage({ - Key key, + Key? key, }) : super(key: key); @override _TermPageState createState() => _TermPageState(); } class _TermPageState extends State { - ZefyrController _controller; - FocusNode _focusNode; - NotusDocument document = new NotusDocument(); + // ZefyrController _controller; + // FocusNode _focusNode; + // NotusDocument document = new NotusDocument(); bool isLoading = false; @override void initState() { super.initState(); - _focusNode = FocusNode(); + // _focusNode = FocusNode(); } - NotusDocument _loadDocument(Setting setting) { - bool isEng = Provider.of(context).isEng; - String term = isEng ? setting.termsEng : setting.termsMm; - NotusDocument doc; - try { - doc = NotusDocument.fromJson(jsonDecode(term)); - } catch (e) {} - if (doc == null) { - doc = NotusDocument(); - } - return doc; - } + // NotusDocument _loadDocument(Setting setting) { + // bool isEng = Provider.of(context).isEng; + // String term = isEng ? setting.termsEng : setting.termsMm; + // NotusDocument doc; + // try { + // doc = NotusDocument.fromJson(jsonDecode(term)); + // } catch (e) {} + // if (doc == null) { + // doc = NotusDocument(); + // } + // return doc; + // } @override Widget build(BuildContext context) { - Setting setting = Provider.of(context).setting; + Setting? setting = Provider.of(context).setting; bool isEditable = context.select((MainModel m) => m.termEditable()); return Scaffold( @@ -76,7 +76,7 @@ class _TermPageState extends State { onPressed: () => Navigator.of(context).push(CupertinoPageRoute( builder: (context) => - TermEdit(term: Term.fromSetting(setting)), + TermEdit(term: Term.fromSetting(setting!)), )), icon: Icon( CupertinoIcons.pen, @@ -85,25 +85,25 @@ class _TermPageState extends State { ] : [], ), - body: ZefyrTheme( - data: ZefyrThemeData().copyWith( - defaultLineTheme: LineTheme( - padding: EdgeInsets.all(0), - textStyle: TextStyle(fontFamily: "Myanmar3"), - ), - ), - // data: ZefyrThemeData().copyWith( - // defaultLineTheme: LineTheme( - // textStyle: TextStyle(color: Colors.black), - // padding: EdgeInsets.all(0))), - child: ZefyrScaffold( - child: ZefyrEditor( - mode: ZefyrMode.view, - padding: EdgeInsets.all(16), - controller: ZefyrController(_loadDocument(setting)), - focusNode: _focusNode, - ), - )), + // body: ZefyrTheme( + // data: ZefyrThemeData().copyWith( + // defaultLineTheme: LineTheme( + // padding: EdgeInsets.all(0), + // textStyle: TextStyle(fontFamily: "Myanmar3"), + // ), + // ), + // // data: ZefyrThemeData().copyWith( + // // defaultLineTheme: LineTheme( + // // textStyle: TextStyle(color: Colors.black), + // // padding: EdgeInsets.all(0))), + // child: ZefyrScaffold( + // child: ZefyrEditor( + // mode: ZefyrMode.view, + // padding: EdgeInsets.all(16), + // controller: ZefyrController(_loadDocument(setting)), + // focusNode: _focusNode, + // ), + // )), ); } } diff --git a/lib/pages/widgets/my_data_table.dart b/lib/pages/widgets/my_data_table.dart index 8e66eef..cb67101 100644 --- a/lib/pages/widgets/my_data_table.dart +++ b/lib/pages/widgets/my_data_table.dart @@ -83,12 +83,16 @@ class MyDataRow { /// /// The [cells] argument must not be null. MyDataRow.byIndex({ +<<<<<<< HEAD int index = 0, +======= + int? index, +>>>>>>> upstream/master this.selected = false, this.onSelectChanged, required this.cells, }) : assert(cells != null), - key = ValueKey(index); + key = ValueKey(index!); /// A [Key] that uniquely identifies this row. This is used to /// ensure that if a row is added or removed, any stateful widgets @@ -320,7 +324,7 @@ class MyDataTable extends StatelessWidget { this.columnSpacing = 56.0, this.oddLine, this.evenLine, - @required this.rows, + required this.rows, }) : assert(columns != null), assert(columns.isNotEmpty), assert(sortColumnIndex == null || @@ -451,10 +455,10 @@ class MyDataTable extends StatelessWidget { Color(0x1E000000); // Dark theme variant is just a guess. Widget _buildCheckbox({ - Color color, - bool checked, - VoidCallback onRowTap, - ValueChanged onCheckboxChanged, + Color? color, + bool? checked, + VoidCallback? onRowTap, + ValueChanged? onCheckboxChanged, }) { Widget contents = Semantics( container: true, @@ -465,7 +469,9 @@ class MyDataTable extends StatelessWidget { child: Checkbox( activeColor: color, value: checked, - onChanged: onCheckboxChanged, + onChanged: (bool? value) { + onCheckboxChanged!(value ?? false); + }, ), ), ), @@ -483,32 +489,33 @@ class MyDataTable extends StatelessWidget { } Widget _buildHeadingCell({ - BuildContext context, - EdgeInsetsGeometry padding, - Widget label, - String tooltip, - bool numeric, - VoidCallback onSort, - bool sorted, - bool ascending, + required BuildContext context, + EdgeInsetsGeometry? padding, + Widget? label, + String? tooltip, + bool? numeric, + VoidCallback? onSort, + bool? sorted, + bool? ascending, }) { if (onSort != null) { final Widget arrow = _SortArrow( - visible: sorted, + visible: sorted!, down: sorted ? ascending : null, duration: _sortArrowAnimationDuration, ); const Widget arrowPadding = SizedBox(width: _sortArrowPadding); label = Row( - textDirection: numeric ? TextDirection.rtl : null, - children: [label, arrowPadding, arrow], + textDirection: (numeric ?? false) ? TextDirection.rtl : null, + children: [label ?? Container(), arrowPadding, arrow], ); } label = Container( padding: padding, height: headingRowHeight, - alignment: - numeric ? Alignment.centerRight : AlignmentDirectional.centerStart, + alignment: (numeric ?? false) + ? Alignment.centerRight + : AlignmentDirectional.centerStart, child: AnimatedDefaultTextStyle( style: TextStyle( // TODO(ianh): font family should match Theme; see https://github.com/flutter/flutter/issues/3116 @@ -516,12 +523,16 @@ class MyDataTable extends StatelessWidget { fontSize: _headingFontSize, height: math.min(1.0, headingRowHeight / _headingFontSize), color: (Theme.of(context).brightness == Brightness.light) - ? ((onSort != null && sorted) ? Colors.black87 : Colors.black54) - : ((onSort != null && sorted) ? Colors.white : Colors.white70), + ? ((onSort != null && (sorted ?? false)) + ? Colors.black87 + : Colors.black54) + : ((onSort != null && (sorted ?? false)) + ? Colors.white + : Colors.white70), ), softWrap: false, duration: _sortArrowAnimationDuration, - child: label, + child: label ?? Container(), ), ); if (tooltip != null) { @@ -540,42 +551,43 @@ class MyDataTable extends StatelessWidget { } Widget _buildMyDataCell({ - BuildContext context, - EdgeInsetsGeometry padding, - Widget label, - bool numeric, - bool placeholder, - bool showEditIcon, - VoidCallback onTap, - VoidCallback onSelectChanged, + required BuildContext context, + EdgeInsetsGeometry? padding, + Widget? label, + bool? numeric, + bool? placeholder, + bool? showEditIcon, + VoidCallback? onTap, + VoidCallback? onSelectChanged, }) { final bool isLightTheme = Theme.of(context).brightness == Brightness.light; - if (showEditIcon) { + if (showEditIcon ?? false) { const Widget icon = Icon(Icons.edit, size: 18.0); - label = Expanded(child: label); + label = Expanded(child: label ?? Container()); label = Row( - textDirection: numeric ? TextDirection.rtl : null, + textDirection: (numeric ?? false) ? TextDirection.rtl : null, children: [label, icon], ); } label = Container( padding: padding, height: MyDataRowHeight, - alignment: - numeric ? Alignment.centerRight : AlignmentDirectional.centerStart, + alignment: (numeric ?? false) + ? Alignment.centerRight + : AlignmentDirectional.centerStart, child: DefaultTextStyle( style: TextStyle( // TODO(ianh): font family should be Roboto; see https://github.com/flutter/flutter/issues/3116 fontSize: 13.0, color: isLightTheme - ? (placeholder ? Colors.black38 : Colors.black87) - : (placeholder ? Colors.white38 : Colors.white70), + ? ((placeholder ?? false) ? Colors.black38 : Colors.black87) + : ((placeholder ?? false) ? Colors.white38 : Colors.white70), ), child: IconTheme.merge( data: IconThemeData( color: isLightTheme ? Colors.black54 : Colors.white70, ), - child: DropdownButtonHideUnderline(child: label), + child: DropdownButtonHideUnderline(child: label ?? Container()), ), ), ); @@ -614,6 +626,10 @@ class MyDataTable extends StatelessWidget { final List tableColumns = (columns.length + (showCheckboxColumn ? 1 : 0)) as List; +<<<<<<< HEAD +======= + +>>>>>>> upstream/master final List tableRows = List.generate( rows.length + 1, // the +1 is for the header row (int index) { @@ -626,36 +642,36 @@ class MyDataTable extends StatelessWidget { : index.isEven && evenLine != null ? evenLine : _kUnselectedDecoration, - children: List(tableColumns.length), + children: tableColumns.map((e) => Container()).toList(), ); }, ); int rowIndex; - int displayColumnIndex = 0; - if (showCheckboxColumn) { - tableColumns[0] = FixedColumnWidth( - horizontalMargin + Checkbox.width + horizontalMargin / 2.0); - tableRows[0].children[0] = _buildCheckbox( - color: theme.accentColor, - checked: allChecked, - onCheckboxChanged: _handleSelectAll, - ); - rowIndex = 1; - for (MyDataRow row in rows) { - tableRows[rowIndex].children[0] = _buildCheckbox( - color: theme.accentColor, - checked: row.selected, - onRowTap: () => row.onSelectChanged != null - ? row.onSelectChanged(!row.selected) - : null, - onCheckboxChanged: row.onSelectChanged, - ); - rowIndex += 1; - } - displayColumnIndex += 1; - } + int displayColumnIndex = 0; + // if (showCheckboxColumn) { + // tableColumns[0] = FixedColumnWidth( + // horizontalMargin + Checkbox.width + horizontalMargin / 2.0); + // tableRows[0].children![0] = _buildCheckbox( + // color: theme.accentColor, + // checked: allChecked, + // onCheckboxChanged: _handleSelectAll, + // ); + // rowIndex = 1; + // for (MyDataRow row in rows) { + // tableRows[rowIndex].children[0] = _buildCheckbox( + // color: theme.accentColor, + // checked: row.selected, + // onRowTap: () => row.onSelectChanged != null + // ? row.onSelectChanged(!row.selected) + // : null, + // onCheckboxChanged: row.onSelectChanged, + // ); + // rowIndex += 1; + // } + // displayColumnIndex += 1; + // } for (int MyDataColumnIndex = 0; MyDataColumnIndex < columns.length; @@ -688,14 +704,14 @@ class MyDataTable extends StatelessWidget { } else { tableColumns[displayColumnIndex] = const IntrinsicColumnWidth(); } - tableRows[0].children[displayColumnIndex] = _buildHeadingCell( + tableRows[0].children![displayColumnIndex] = _buildHeadingCell( context: context, padding: padding, label: column.label, tooltip: column.tooltip, numeric: column.numeric, onSort: () => column.onSort != null - ? column.onSort(MyDataColumnIndex, + ? column.onSort!(MyDataColumnIndex, sortColumnIndex != MyDataColumnIndex || !sortAscending) : null, sorted: MyDataColumnIndex == sortColumnIndex, @@ -704,7 +720,7 @@ class MyDataTable extends StatelessWidget { rowIndex = 1; for (MyDataRow row in rows) { final MyDataCell cell = row.cells[MyDataColumnIndex]; - tableRows[rowIndex].children[displayColumnIndex] = _buildMyDataCell( + tableRows[rowIndex].children?[displayColumnIndex] = _buildMyDataCell( context: context, padding: padding, label: cell.child, @@ -713,7 +729,7 @@ class MyDataTable extends StatelessWidget { showEditIcon: cell.showEditIcon, onTap: cell.onTap, onSelectChanged: () => row.onSelectChanged != null - ? row.onSelectChanged(!row.selected) + ? row.onSelectChanged!(!row.selected) : null, ); rowIndex += 1; @@ -745,11 +761,19 @@ class TableRowInkWell extends InkResponse { /// Creates an ink well for a table row. const TableRowInkWell({ Key? key, +<<<<<<< HEAD Widget child, GestureTapCallback onTap, GestureTapCallback onDoubleTap, GestureLongPressCallback onLongPress, ValueChanged onHighlightChanged, +======= + Widget? child, + GestureTapCallback? onTap, + GestureTapCallback? onDoubleTap, + GestureLongPressCallback? onLongPress, + ValueChanged? onHighlightChanged, +>>>>>>> upstream/master }) : super( key: key, child: child, @@ -765,7 +789,7 @@ class TableRowInkWell extends InkResponse { RectCallback getRectCallback(RenderBox referenceBox) { return () { RenderObject cell = referenceBox; - AbstractNode table = cell.parent; + AbstractNode? table = cell.parent; final Matrix4 transform = Matrix4.identity(); while (table is RenderObject && table is! RenderTable) { final RenderObject parentBox = table as RenderObject; @@ -778,11 +802,11 @@ class TableRowInkWell extends InkResponse { final TableCellParentData cellParentData = cell.parentData as TableCellParentData; assert(cellParentData.y != null); - final Rect rect = table.getRowBox(cellParentData.y); + final Rect rect = table.getRowBox(cellParentData.y!); // The rect is in the table's coordinate space. We need to change it to the // TableRowInkWell's coordinate space. table.applyPaintTransform(cell, transform); - final Offset offset = MatrixUtils.getAsTranslation(transform); + final Offset? offset = MatrixUtils.getAsTranslation(transform); if (offset != null) return rect.shift(-offset); } return Rect.zero; @@ -804,17 +828,18 @@ class _SortArrow extends StatefulWidget { this.duration, }) : super(key: key); - final bool visible; + final bool? visible; - final bool down; + final bool? down; - final Duration duration; + final Duration? duration; @override _SortArrowState createState() => _SortArrowState(); } class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { +<<<<<<< HEAD late AnimationController _opacityController; late Animation _opacityAnimation; @@ -823,6 +848,16 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { double _orientationOffset = 0.0; late bool _down; +======= + AnimationController? _opacityController; + Animation? _opacityAnimation; + + AnimationController? _orientationController; + Animation? _orientationAnimation; + double _orientationOffset = 0.0; + + bool? _down; +>>>>>>> upstream/master static final Animatable _turnTween = Tween(begin: 0.0, end: math.pi) @@ -838,15 +873,16 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { ), curve: Curves.fastOutSlowIn, )..addListener(_rebuild); - _opacityController.value = widget.visible ? 1.0 : 0.0; + _opacityController!.value = (widget.visible ?? false) ? 1.0 : 0.0; _orientationController = AnimationController( duration: widget.duration, vsync: this, ); - _orientationAnimation = _orientationController.drive(_turnTween) + _orientationAnimation = _orientationController!.drive(_turnTween) ..addListener(_rebuild) ..addStatusListener(_resetOrientationAnimation); - if (widget.visible) _orientationOffset = widget.down ? 0.0 : math.pi; + if (widget.visible ?? false) + _orientationOffset = (widget.down ?? false) ? 0.0 : math.pi; } void _rebuild() { @@ -857,9 +893,9 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { void _resetOrientationAnimation(AnimationStatus status) { if (status == AnimationStatus.completed) { - assert(_orientationAnimation.value == math.pi); + assert(_orientationAnimation!.value == math.pi); _orientationOffset += math.pi; - _orientationController.value = + _orientationController!.value = 0.0; // TODO(ianh): This triggers a pointless rebuild. } } @@ -868,26 +904,30 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { void didUpdateWidget(_SortArrow oldWidget) { super.didUpdateWidget(oldWidget); bool skipArrow = false; +<<<<<<< HEAD final bool newDown = widget.down; +======= + final bool newDown = widget.down ?? _down!; +>>>>>>> upstream/master if (oldWidget.visible != widget.visible) { - if (widget.visible && - (_opacityController.status == AnimationStatus.dismissed)) { - _orientationController.stop(); - _orientationController.value = 0.0; + if (widget.visible! && + (_opacityController!.status == AnimationStatus.dismissed)) { + _orientationController!.stop(); + _orientationController!.value = 0.0; _orientationOffset = newDown ? 0.0 : math.pi; skipArrow = true; } - if (widget.visible) { - _opacityController.forward(); + if ((widget.visible ?? false)) { + _opacityController!.forward(); } else { - _opacityController.reverse(); + _opacityController!.reverse(); } } if ((_down != newDown) && !skipArrow) { - if (_orientationController.status == AnimationStatus.dismissed) { - _orientationController.forward(); + if (_orientationController!.status == AnimationStatus.dismissed) { + _orientationController?.forward(); } else { - _orientationController.reverse(); + _orientationController?.reverse(); } } _down = newDown; @@ -895,8 +935,8 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { @override void dispose() { - _opacityController.dispose(); - _orientationController.dispose(); + _opacityController?.dispose(); + _orientationController?.dispose(); super.dispose(); } @@ -906,10 +946,10 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin { @override Widget build(BuildContext context) { return Opacity( - opacity: _opacityAnimation.value, + opacity: _opacityAnimation!.value, child: Transform( transform: - Matrix4.rotationZ(_orientationOffset + _orientationAnimation.value) + Matrix4.rotationZ(_orientationOffset + _orientationAnimation!.value) ..setTranslationRaw(0.0, _arrowIconBaselineOffset, 0.0), alignment: Alignment.center, child: Icon( diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..b2eaaba --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,116 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "fcs") +set(APPLICATION_ID "com.mokkon.fcs.fcs") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..33fd580 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..026851f --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..9bf7478 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..1fc8ed3 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 0000000..a2cc618 --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,105 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "fcs"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "fcs"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/pubspec.lock b/pubspec.lock index 2901b05..c317df9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -70,7 +70,7 @@ packages: name: camera url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.9.2+2" camera_platform_interface: dependency: transitive description: @@ -196,7 +196,7 @@ packages: name: cross_file url: "https://pub.dartlang.org" source: hosted - version: "0.3.1+4" + version: "0.3.1+5" crypto: dependency: transitive description: @@ -259,21 +259,21 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.1" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "6.0.1" + version: "6.1.0" firebase_auth_web: dependency: transitive description: name: firebase_auth_web url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.1.0" firebase_core: dependency: "direct main" description: @@ -295,27 +295,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + url: "https://pub.dartlang.org" + source: hosted + version: "10.0.7" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" firebase_storage: dependency: "direct main" description: name: firebase_storage url: "https://pub.dartlang.org" source: hosted - version: "10.0.2" + version: "10.0.3" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.0.2" firebase_storage_web: dependency: transitive description: name: firebase_storage_web url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" fixnum: dependency: transitive description: @@ -388,7 +409,7 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -454,7 +475,7 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.8.3+3" + version: "0.8.4" image_picker_for_web: dependency: transitive description: @@ -468,7 +489,7 @@ packages: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.4.1" intl: dependency: "direct main" description: @@ -573,7 +594,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" path_provider_linux: dependency: transitive description: @@ -615,7 +636,7 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "8.1.4+2" + version: "8.1.6" permission_handler_platform_interface: dependency: transitive description: @@ -706,7 +727,7 @@ packages: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.27.1" + version: "0.27.2" share: dependency: "direct main" description: @@ -823,7 +844,7 @@ packages: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.1+1" stack_trace: dependency: transitive description: @@ -893,7 +914,7 @@ packages: name: timeline_list url: "https://pub.dartlang.org" source: hosted - version: "0.0.5" + version: "0.0.6" timezone: dependency: transitive description: @@ -921,21 +942,21 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.9" + version: "6.0.10" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" url_launcher_platform_interface: dependency: transitive description: @@ -1005,7 +1026,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.6" + version: "2.2.9" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e60aa6c..260c466 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,11 +13,11 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.3 - firebase_core: ^1.5.0 + firebase_core: ^1.6.0 cloud_firestore: ^2.5.2 - firebase_storage: ^10.0.2 - firebase_auth: "^3.0.2" - # firebase_messaging: ^6.0.15 + firebase_storage: ^10.0.3 + firebase_auth: ^3.1.1 + firebase_messaging: ^10.0.7 provider: ^6.0.0 image_picker: ^0.8.3+3 @@ -72,5 +72,4 @@ flutter: - family: Myanmar3 fonts: - - asset: assets/fonts/Myanmar3_2018.ttf - + - asset: assets/fonts/Myanmar3_2018.ttf \ No newline at end of file diff --git a/test/rate_test.dart b/test/rate_test.dart deleted file mode 100644 index a373e2c..0000000 --- a/test/rate_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:fcs/domain/entities/cargo_type.dart'; -import 'package:fcs/domain/entities/carton.dart'; -import 'package:fcs/domain/entities/discount_by_weight.dart'; -import 'package:fcs/domain/entities/rate.dart'; -import 'package:test/test.dart'; - -@TestOn('vm') -void main() { - var rate = Rate(); - rate.discountByWeights = [ - DiscountByWeight(weight: 25, discount: 0.25), - DiscountByWeight(weight: 50, discount: 0.5), - ]; - rate.volumetricRatio = 166.36; - rate.diffDiscountWeight = 5; - rate.diffWeightRate = 1.5; - - test('Calculate carton price 1', () { - var ct = CargoType(name: "General", weight: 10, rate: 6); - var carton = Carton(cargoTypes: [ct], length: 15, width: 15, height: 15); - expect(carton.calAmount(rate), equals(67.50)); - }); - test('Calculate carton price 2', () { - var ct = CargoType(name: "General", weight: 10, rate: 6); - var carton = Carton(cargoTypes: [ct], length: 10, width: 10, height: 10); - expect(carton.calAmount(rate), equals(60)); - }); - test('Calculate carton price 3', () { - var ct = CargoType(name: "General", weight: 10, rate: 6); - var carton = Carton(cargoTypes: [ct], length: 15, width: 15, height: 10); - expect(carton.calAmount(rate), equals(60)); - }); -} diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..6eea97d --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:fcs/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..8387474 --- /dev/null +++ b/web/index.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + fcs + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..9f0d80b --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "fcs", + "short_name": "fcs", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +}