This commit is contained in:
tzw
2021-09-11 10:32:50 +06:30
75 changed files with 1112 additions and 522 deletions

View File

@@ -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<dynamic> backgroundMessageHandler(Map<String, dynamic> 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<void> 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<String, dynamic> message) async {
log.info("onMessage: $message");
if (onMessage != null) _onNotify(message, onMessage);
},
onBackgroundMessage: Platform.isIOS ? null : backgroundMessageHandler,
onLaunch: (Map<String, dynamic> message) async {
log.info("onLaunch: $message");
if (onLaunch != null) _onNotify(message, onLaunch);
},
onResume: (Map<String, dynamic> 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<void> subscribeToTopic(String topic) {

View File

@@ -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,

View File

@@ -28,7 +28,7 @@ class Invoice {
List<CustomDuty> customDuties;
List<Carton> cartons;
List<CargoType> cargoTypes;
List<Shipment> shipments;
List<Shipment?>? shipments;
List<Payment> 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(),

View File

@@ -36,7 +36,7 @@ class Package {
DeliveryAddress? deliveryAddress;
//for packages in processing
List<File> photoFiles;
List<File?> photoFiles;
int get amount => rate != null && weight != null ? rate * weight : 0;

View File

@@ -24,20 +24,24 @@ Future<Map?> getClaims({bool refreshIdToken = false}) async {
}
// returns list of url
Future<List<String>> uploadFiles(String path, List<File> files,
Future<List<String>> uploadFiles(String path, List<File?> files,
{String? fileName}) async {
List<Future<String>> fu = [];
for (File f in files) {
for (File? f in files) {
Future<String> u = uploadStorage(path, f);
fu.add(u);
}
return Future.wait(fu);
}
Future<String> uploadStorage(String path, File file, {String? fileName}) async {
Future<String> 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<String> uploadStorage(String path, File file, {String? fileName}) async {
// return downloadUrl;
}
Future<void> deleteStorageFromUrls(List<String> urls) async {
Future<void> deleteStorageFromUrls(List<String?> 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]!);
}
}

View File

@@ -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,

113
lib/main.dart Normal file
View File

@@ -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<MyHomePage> {
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: <Widget>[
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.
);
}
}

View File

@@ -39,7 +39,7 @@ class _CargoTypeEditorState extends State<CargoTypeEditor> {
_loadDefalut() {
ShipmentRateModel shipmentRateModel =
Provider.of<ShipmentRateModel>(context, listen: false);
_cargo = shipmentRateModel.rate.defaultCargoType?.clone();
_cargo = shipmentRateModel.rate.defaultCargoType.clone();
}
@override

View File

@@ -171,7 +171,7 @@ class PartSearchDelegate extends SearchDelegate<Carton> {
// }
try {
String barcode = await scanBarcode();
String? barcode = await scanBarcode();
if (barcode != null) {
query = barcode;
showResults(context);

View File

@@ -103,10 +103,10 @@ class _InvoiceEditorState extends State<InvoiceEditor> {
_loadShipments() async {
ShipmentModel shipmentModel =
Provider.of<ShipmentModel>(context, listen: false);
List<Shipment> shipments = await shipmentModel.getShipmentWithHandlingFee(
List<Shipment?>? 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<InvoiceEditor> {
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<InvoiceEditor> {
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<InvoiceEditor> {
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;

View File

@@ -9,7 +9,7 @@ typedef OnAdd(Shipment shipment);
typedef OnRemove(Shipment shipment);
class InvoiceHandlingFeeList extends StatelessWidget {
final List<Shipment>? shipments;
final List<Shipment?>? 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(

View File

@@ -39,8 +39,8 @@ class _InvoiceInfoState extends State<InvoiceInfo> {
void initState() {
super.initState();
_invoice = widget.invoice!;
_invoice!.shipments.forEach((s) {
s.isSelected = true;
_invoice!.shipments!.forEach((s) {
s!.isSelected = true;
});
_loadCartons();
}

View File

@@ -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)}"));
});

View File

@@ -260,7 +260,7 @@ class PackageModel extends BaseModel {
}
Future<void> createReceiving(
User user, Package package, List<File> files) async {
User user, Package package, List<File?> files) async {
if (user != null) {
package.fcsID = user.fcsID;
}
@@ -288,22 +288,21 @@ class PackageModel extends BaseModel {
}
}
Future<void> updateReceiving(User user, Package package, List<File> files,
List<String> deletedUrls) async {
Future<void> updateReceiving(User user, Package package, List<File?> files,
List<String?> 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<String> 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<void> updateProcessing(
Package package, List<File> files, List<String> deletedUrls) async {
Package package, List<File?> files, List<String?> deletedUrls) async {
if (deletedUrls != null) {
for (String url in deletedUrls) {
for (String? url in deletedUrls) {
package.photoUrls.remove(url);
}
}
List<String> 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");

View File

@@ -164,7 +164,7 @@ class _TrackingIDPageState extends State<TrackingIDPage> {
// }
try {
String barcode = await scanBarcode();
String? barcode = await scanBarcode();
if (barcode != null) {
setState(() {
_transcationIDCtl.text = barcode;

View File

@@ -147,7 +147,7 @@ class PackageSearchDelegate extends SearchDelegate<Package> {
// 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);

View File

@@ -80,7 +80,7 @@ class _ProfileState extends State<Profile> {
);
final phonenumberbox = DisplayText(
text: mainModel.user!.phone ?? "",
text: mainModel.user!.phone,
labelTextKey: "profile.phone",
iconData: Icons.phone,
);

View File

@@ -36,7 +36,7 @@ class _CustomEditorState extends State<CustomEditor> {
_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;
}

View File

@@ -86,7 +86,7 @@ class _CustomListState extends State<CustomList> {
custom.rate == null
? ""
: "Shipment rate \$ " +
custom.rate!.toStringAsFixed(2)),
custom.rate.toStringAsFixed(2)),
),
);
}),

View File

@@ -62,11 +62,11 @@ class _ShipmentRatesCalState extends State<ShipmentRatesCal> {
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();
});

View File

@@ -212,7 +212,7 @@ class _ReceivingEditorState extends State<ReceivingEditor> {
// }
try {
String barcode = await scanBarcode();
String? barcode = await scanBarcode();
if (barcode != null) {
setState(() {
_trackingIDCtl.text = barcode;

View File

@@ -56,7 +56,7 @@ class _ShipmentAssignState extends State<ShipmentAssign> {
_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";

View File

@@ -37,7 +37,7 @@ class _ShipmentConfirmState extends State<ShipmentConfirm> {
super.initState();
_shipment = widget.shipment;
_handlingFee.text = _shipment!.handlingFee?.toString() ?? "0";
_handlingFee.text = _shipment!.handlingFee.toString();
}
@override

View File

@@ -223,7 +223,7 @@ class _ShipmentInfoState extends State<ShipmentInfo> {
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(

View File

@@ -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<TermEdit> {
/// 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<TermEdit> {
_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<TermEdit> {
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<TermEdit> {
);
}
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<TermEdit> {
_isLoading = true;
});
try {
final contentsEng = jsonEncode(_controllerEng.document);
final contentsMm = jsonEncode(_controllerMm.document);
print('contents => $contentsEng');
TermModel termModel = Provider.of<TermModel>(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<TermModel>(context, listen: false);
// await termModel.saveTerm(Term(termEng: contentsEng, termMm: contentsMm));
} catch (e) {
showMsgDialog(context, "Error", e.toString());
} finally {

View File

@@ -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<TermPage> {
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<LanguageModel>(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<LanguageModel>(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<MainModel>(context).setting;
Setting? setting = Provider.of<MainModel>(context).setting;
bool isEditable = context.select((MainModel m) => m.termEditable());
return Scaffold(
@@ -76,7 +76,7 @@ class _TermPageState extends State<TermPage> {
onPressed: () =>
Navigator.of(context).push<void>(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<TermPage> {
]
: [],
),
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,
// ),
// )),
);
}
}

View File

@@ -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<int>(index);
key = ValueKey<int>(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<bool> onCheckboxChanged,
Color? color,
bool? checked,
VoidCallback? onRowTap,
ValueChanged<bool>? 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: <Widget>[label, arrowPadding, arrow],
textDirection: (numeric ?? false) ? TextDirection.rtl : null,
children: <Widget>[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: <Widget>[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<TableColumnWidth> tableColumns = (columns.length +
(showCheckboxColumn ? 1 : 0)) as List<TableColumnWidth>;
<<<<<<< HEAD
=======
>>>>>>> upstream/master
final List<TableRow> tableRows = List<TableRow>.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<Widget>(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<bool> onHighlightChanged,
=======
Widget? child,
GestureTapCallback? onTap,
GestureTapCallback? onDoubleTap,
GestureLongPressCallback? onLongPress,
ValueChanged<bool>? 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<double> _opacityAnimation;
@@ -823,6 +848,16 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
double _orientationOffset = 0.0;
late bool _down;
=======
AnimationController? _opacityController;
Animation<double>? _opacityAnimation;
AnimationController? _orientationController;
Animation<double>? _orientationAnimation;
double _orientationOffset = 0.0;
bool? _down;
>>>>>>> upstream/master
static final Animatable<double> _turnTween =
Tween<double>(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(