add packages
This commit is contained in:
@@ -105,7 +105,15 @@
|
|||||||
"package.create.packages":"Complete receiving",
|
"package.create.packages":"Complete receiving",
|
||||||
"package.create.market":"Market",
|
"package.create.market":"Market",
|
||||||
|
|
||||||
"package.edit.title":"PACKAGE",
|
"package.edit.status":"Status",
|
||||||
|
"package.edit.title":"Edit Package",
|
||||||
|
"package.edit.remark":"Remark",
|
||||||
|
"package.edit.desc":"Description",
|
||||||
|
"package.edit.complete.process.btn":"Complete processing",
|
||||||
|
"package.edit.procseeing":"Processing",
|
||||||
|
|
||||||
|
"package.info.title":"Package",
|
||||||
|
|
||||||
"package.arrival.date":"Arrival Date",
|
"package.arrival.date":"Arrival Date",
|
||||||
"package.number":"Box Number",
|
"package.number":"Box Number",
|
||||||
"package.rate":"Rate",
|
"package.rate":"Rate",
|
||||||
|
|||||||
@@ -104,10 +104,17 @@
|
|||||||
"package.create.phone":"ဖုန်းနံပါတ်",
|
"package.create.phone":"ဖုန်းနံပါတ်",
|
||||||
"package.tracking.id":"Tracking ID",
|
"package.tracking.id":"Tracking ID",
|
||||||
"package.create.packages":"အထုပ် အသစ်များ လက်ခံမည်",
|
"package.create.packages":"အထုပ် အသစ်များ လက်ခံမည်",
|
||||||
"package.create.market":"Market",
|
"package.create.market":"အွန်လိုင်စျေးဆိုင်",
|
||||||
|
|
||||||
|
"package.edit.title":"အထုပ် ပြင်ဆင်ခြင်း",
|
||||||
|
"package.edit.remark":"မှတ်ချက်",
|
||||||
|
"package.edit.desc":"ဖော်ပြချက်",
|
||||||
|
"package.edit.complete.process.btn":"မွမ်းမံခြင်း ပြီးဆုံးသည်",
|
||||||
|
"package.edit.status":"အခြေအနေ",
|
||||||
|
"package.edit.procseeing":"မွမ်းမံခြင်း",
|
||||||
|
|
||||||
|
"package.info.title":"အထုပ်",
|
||||||
|
|
||||||
"package.new":"New Package",
|
|
||||||
"package.edit.title":"PACKAGE",
|
|
||||||
"package.arrival.date":"Arrival Date",
|
"package.arrival.date":"Arrival Date",
|
||||||
"package.number":"Package Number",
|
"package.number":"Package Number",
|
||||||
"package.rate":"Rate",
|
"package.rate":"Rate",
|
||||||
|
|||||||
@@ -126,20 +126,23 @@ class AuthFb {
|
|||||||
log.info("Claims:${idToken.claims}");
|
log.info("Claims:${idToken.claims}");
|
||||||
|
|
||||||
User user = User();
|
User user = User();
|
||||||
user.id = firebaseUser.uid;
|
user.status = idToken.claims["st"];
|
||||||
user.status = idToken.claims["status"];
|
|
||||||
user.phoneNumber = firebaseUser.phoneNumber;
|
user.phoneNumber = firebaseUser.phoneNumber;
|
||||||
|
|
||||||
// add privileges
|
// add privileges
|
||||||
String privileges = idToken.claims["privileges"];
|
String privileges = idToken.claims["pr"];
|
||||||
if (privileges != null && privileges != "") {
|
if (privileges != null && privileges != "") {
|
||||||
user.privileges = privileges.split(":").toList();
|
user.privileges = privileges.split(":").toList();
|
||||||
}
|
}
|
||||||
User _user = await getUserFromFirestore(user.id);
|
String cid = idToken.claims["cid"];
|
||||||
|
if (cid != null && cid != "") {
|
||||||
|
User _user = await getUserFromFirestore(cid);
|
||||||
if (_user != null) {
|
if (_user != null) {
|
||||||
|
user.id = cid;
|
||||||
user.fcsID = _user.fcsID;
|
user.fcsID = _user.fcsID;
|
||||||
user.name = _user.name;
|
user.name = _user.name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +173,16 @@ class AuthFb {
|
|||||||
return getUser(refreshIdToken: true);
|
return getUser(refreshIdToken: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<User> joinInvite(String userName) async {
|
||||||
|
await requestAPI("/join_invite", "POST",
|
||||||
|
payload: {
|
||||||
|
'user_name': userName,
|
||||||
|
},
|
||||||
|
token: await getToken());
|
||||||
|
// refresh token once signup
|
||||||
|
return getUser(refreshIdToken: true);
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> hasInvite() async {
|
Future<bool> hasInvite() async {
|
||||||
var invited =
|
var invited =
|
||||||
await requestAPI("/check_invitation", "GET", token: await getToken());
|
await requestAPI("/check_invitation", "GET", token: await getToken());
|
||||||
@@ -210,4 +223,17 @@ class AuthFb {
|
|||||||
yield setting;
|
yield setting;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<User> user(String userID) async* {
|
||||||
|
Stream<DocumentSnapshot> snapshot = Firestore.instance
|
||||||
|
.collection(user_collection)
|
||||||
|
.document(userID)
|
||||||
|
.snapshots();
|
||||||
|
|
||||||
|
await for (var snap in snapshot) {
|
||||||
|
User user = User.fromMap(snap.data, snap.documentID);
|
||||||
|
user = await getUser(refreshIdToken: true);
|
||||||
|
yield user;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class UserDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> acceptRequest(String userID) async {
|
Future<void> acceptRequest(String userID) async {
|
||||||
return await requestAPI("/invites", "PUT",
|
return await requestAPI("/accept_request", "PUT",
|
||||||
payload: {"id": userID}, token: await getToken());
|
payload: {"id": userID}, token: await getToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ const privilege_collection = "privileges";
|
|||||||
const markets_collection = "markets";
|
const markets_collection = "markets";
|
||||||
const packages_collection = "packages";
|
const packages_collection = "packages";
|
||||||
|
|
||||||
|
const user_requested_status = "requested";
|
||||||
|
const user_invited_status = "invited";
|
||||||
|
|
||||||
|
const pkg_files_path = "/packages";
|
||||||
|
|
||||||
const ok_doc_id = "ok";
|
const ok_doc_id = "ok";
|
||||||
|
|
||||||
const biz_collection = "bizs";
|
const biz_collection = "bizs";
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ class Package {
|
|||||||
String phoneNumber;
|
String phoneNumber;
|
||||||
String currentStatus;
|
String currentStatus;
|
||||||
DateTime currentStatusDate;
|
DateTime currentStatusDate;
|
||||||
List<Map<String, dynamic>> allStatus;
|
|
||||||
List<String> photoUrls;
|
List<String> photoUrls;
|
||||||
|
List<ShipmentStatus> shipmentHistory;
|
||||||
|
String desc;
|
||||||
|
|
||||||
String status;
|
String status;
|
||||||
String shipmentNumber;
|
String shipmentNumber;
|
||||||
@@ -39,8 +40,6 @@ class Package {
|
|||||||
shipmentNumber + "-" + receiverNumber + " #" + boxNumber;
|
shipmentNumber + "-" + receiverNumber + " #" + boxNumber;
|
||||||
double get price => rate.toDouble() * weight;
|
double get price => rate.toDouble() * weight;
|
||||||
|
|
||||||
List<ShipmentStatus> shipmentHistory;
|
|
||||||
|
|
||||||
Package({
|
Package({
|
||||||
this.id,
|
this.id,
|
||||||
this.trackingID,
|
this.trackingID,
|
||||||
@@ -66,20 +65,20 @@ class Package {
|
|||||||
this.cargoDesc,
|
this.cargoDesc,
|
||||||
this.market,
|
this.market,
|
||||||
this.shipmentHistory,
|
this.shipmentHistory,
|
||||||
this.allStatus,
|
|
||||||
this.currentStatus,
|
this.currentStatus,
|
||||||
this.currentStatusDate,
|
this.currentStatusDate,
|
||||||
this.photoUrls,
|
this.photoUrls,
|
||||||
|
this.desc,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Package.fromMap(Map<String, dynamic> map, String docID) {
|
factory Package.fromMap(Map<String, dynamic> map, String docID) {
|
||||||
var _currentStatusDate = (map['current_status_date'] as Timestamp);
|
var _currentStatusDate = (map['current_status_date'] as Timestamp);
|
||||||
|
|
||||||
List<Map<String, dynamic>> _allStatus = List.from(map['all_status'])
|
List<ShipmentStatus> _shipmentStatus = List.from(map['all_status'])
|
||||||
.map((e) => Map<String, dynamic>.from(e))
|
.map((e) => ShipmentStatus.fromMap(Map<String, dynamic>.from(e)))
|
||||||
.toList();
|
.toList();
|
||||||
List<String> _photoUrls =
|
List<String> _photoUrls =
|
||||||
map['photo_urls'] == null ? [] : map['photo_urls'].cast<List<String>>();
|
map['photo_urls'] == null ? [] : List.from(map['photo_urls']);
|
||||||
|
|
||||||
return Package(
|
return Package(
|
||||||
id: docID,
|
id: docID,
|
||||||
@@ -89,13 +88,22 @@ class Package {
|
|||||||
market: map['market'],
|
market: map['market'],
|
||||||
userName: map['user_name'],
|
userName: map['user_name'],
|
||||||
phoneNumber: map['phone_number'],
|
phoneNumber: map['phone_number'],
|
||||||
|
remark: map['remark'],
|
||||||
|
desc: map['desc'],
|
||||||
currentStatus: map['current_status'],
|
currentStatus: map['current_status'],
|
||||||
currentStatusDate:
|
currentStatusDate:
|
||||||
_currentStatusDate != null ? _currentStatusDate.toDate() : null,
|
_currentStatusDate != null ? _currentStatusDate.toDate() : null,
|
||||||
photoUrls: _photoUrls,
|
photoUrls: _photoUrls,
|
||||||
allStatus: _allStatus);
|
shipmentHistory: _shipmentStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() =>
|
Map<String, dynamic> toJson() => {
|
||||||
{'id': id, 'tracking_id': trackingID, 'market': market, 'fcs_id': fcsID};
|
'id': id,
|
||||||
|
'tracking_id': trackingID,
|
||||||
|
'market': market,
|
||||||
|
'fcs_id': fcsID,
|
||||||
|
"desc": desc,
|
||||||
|
"remark": remark,
|
||||||
|
"photo_urls": photoUrls
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ class User {
|
|||||||
bool hasPackages() {
|
bool hasPackages() {
|
||||||
return hasSysAdmin() ||
|
return hasSysAdmin() ||
|
||||||
hasAdmin() ||
|
hasAdmin() ||
|
||||||
|
status == userStatusJoined ||
|
||||||
(privileges != null ? privileges.contains('p') : false);
|
(privileges != null ? privileges.contains('p') : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
|
||||||
class ShipmentStatus {
|
class ShipmentStatus {
|
||||||
String status;
|
String status;
|
||||||
DateTime date;
|
DateTime date;
|
||||||
bool done;
|
bool done;
|
||||||
ShipmentStatus({this.status, this.date, this.done});
|
ShipmentStatus({this.status, this.date, this.done});
|
||||||
|
|
||||||
|
factory ShipmentStatus.fromMap(Map<String, dynamic> map) {
|
||||||
|
var _date = (map['date'] as Timestamp);
|
||||||
|
return ShipmentStatus(
|
||||||
|
status: map['status'],
|
||||||
|
date: _date == null ? null : _date.toDate(),
|
||||||
|
done: map['done'],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
final log = Logger('firebaseHelper');
|
final log = Logger('firebaseHelper');
|
||||||
|
|
||||||
@@ -10,3 +14,19 @@ Future<String> getToken() async {
|
|||||||
IdTokenResult token = await firebaseUser.getIdToken();
|
IdTokenResult token = await firebaseUser.getIdToken();
|
||||||
return token.token;
|
return token.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> uploadStorage(String path, File file, {String fileName}) async {
|
||||||
|
if (fileName == null) {
|
||||||
|
fileName = Uuid().v4();
|
||||||
|
}
|
||||||
|
StorageReference storageReference =
|
||||||
|
FirebaseStorage.instance.ref().child('$path/$fileName');
|
||||||
|
StorageUploadTask uploadTask = storageReference.putFile(file);
|
||||||
|
await uploadTask.onComplete;
|
||||||
|
String downloadUrl = await storageReference.getDownloadURL();
|
||||||
|
print("name:${await storageReference.getName()}");
|
||||||
|
print("bucket:${await storageReference.getBucket()}");
|
||||||
|
print("path:${await storageReference.getPath()}");
|
||||||
|
print("meta:${await storageReference.getMetadata()}");
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:fcs/fcs/common/domain/constants.dart';
|
||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/pages/customers/customer_editor.dart';
|
import 'package:fcs/fcs/common/pages/customers/customer_editor.dart';
|
||||||
@@ -12,6 +13,7 @@ import 'package:flutter_icons/flutter_icons.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'invitation_create.dart';
|
||||||
import 'invitation_editor.dart';
|
import 'invitation_editor.dart';
|
||||||
|
|
||||||
class CustomerList extends StatefulWidget {
|
class CustomerList extends StatefulWidget {
|
||||||
@@ -55,25 +57,17 @@ class _CustomerListState extends State<CustomerList> {
|
|||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(BottomUpPageRoute(InvitationCreate()));
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.add),
|
||||||
|
label: LocalText(context, "invitation.new", color: Colors.white),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 3, right: 5),
|
|
||||||
child: InkWell(
|
|
||||||
onTap: _invitations,
|
|
||||||
child: Container(
|
|
||||||
color: primaryColor,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(5.0),
|
|
||||||
child: Text(
|
|
||||||
"Invitations",
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
separatorBuilder: (context, index) => Divider(
|
separatorBuilder: (context, index) => Divider(
|
||||||
@@ -152,7 +146,9 @@ class _CustomerListState extends State<CustomerList> {
|
|||||||
|
|
||||||
Widget _status(String status) {
|
Widget _status(String status) {
|
||||||
return Text(
|
return Text(
|
||||||
status == "requested" ? status : "",
|
(user_requested_status == status || user_invited_status == status)
|
||||||
|
? status
|
||||||
|
: "",
|
||||||
style: TextStyle(color: primaryColor, fontSize: 14),
|
style: TextStyle(color: primaryColor, fontSize: 14),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:fcs/fcs/common/domain/entities/auth_result.dart';
|
import 'package:fcs/fcs/common/domain/entities/auth_result.dart';
|
||||||
import 'package:fcs/fcs/common/domain/entities/auth_status.dart';
|
import 'package:fcs/fcs/common/domain/entities/auth_status.dart';
|
||||||
@@ -34,8 +35,10 @@ class MainModel extends ChangeNotifier {
|
|||||||
this.isOnline = _isOnline;
|
this.isOnline = _isOnline;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
Services.instance.authService.onAuthStatus().listen((event) {
|
Services.instance.authService.onAuthStatus().listen((event) async {
|
||||||
this.user = event;
|
this.user = event;
|
||||||
|
this.user =
|
||||||
|
await Services.instance.authService.getUser(refreshIdToken: true);
|
||||||
_initUser(user);
|
_initUser(user);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
@@ -74,7 +77,7 @@ class MainModel extends ChangeNotifier {
|
|||||||
this.isFirstLaunch = await SharedPref.isFirstLaunch();
|
this.isFirstLaunch = await SharedPref.isFirstLaunch();
|
||||||
this.isFirstLaunch = this.isFirstLaunch ?? true;
|
this.isFirstLaunch = this.isFirstLaunch ?? true;
|
||||||
|
|
||||||
_loadUser();
|
// _loadUser();
|
||||||
this.packageInfo = await PackageInfo.fromPlatform();
|
this.packageInfo = await PackageInfo.fromPlatform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +85,25 @@ class MainModel extends ChangeNotifier {
|
|||||||
models.add(model);
|
models.add(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initUser(User user) {
|
StreamSubscription<User> userListener;
|
||||||
models.forEach((m) => m.initUser(user));
|
|
||||||
|
|
||||||
|
void _initUser(User user) {
|
||||||
|
if (user != null) {
|
||||||
|
if (user.id != null && user.id != "") {
|
||||||
|
if (userListener != null) userListener.cancel();
|
||||||
|
userListener = Services.instance.authService
|
||||||
|
.getUserStream(user.id)
|
||||||
|
.listen((event) {
|
||||||
|
this.user = event;
|
||||||
|
models.forEach((m) => m.initUser(user));
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
models.forEach((m) => m.initUser(user));
|
||||||
|
} else {
|
||||||
|
models.forEach((m) => m.logout());
|
||||||
|
}
|
||||||
|
isLoaded = true;
|
||||||
// if (firebaseMessaging != null) {
|
// if (firebaseMessaging != null) {
|
||||||
// firebaseMessaging.subscribeToTopic(user.docID);
|
// firebaseMessaging.subscribeToTopic(user.docID);
|
||||||
// }
|
// }
|
||||||
@@ -104,15 +123,15 @@ class MainModel extends ChangeNotifier {
|
|||||||
} finally {}
|
} finally {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadUser() async {
|
// void _loadUser() async {
|
||||||
try {
|
// try {
|
||||||
this.user = await Services.instance.authService.getUser();
|
// this.user = await Services.instance.authService.getUser();
|
||||||
_initUser(user);
|
// _initUser(user);
|
||||||
} finally {
|
// } finally {
|
||||||
this.isLoaded = true;
|
// this.isLoaded = true;
|
||||||
notifyListeners();
|
// notifyListeners();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -162,6 +181,13 @@ class MainModel extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> joinInvite(String userName) async {
|
||||||
|
await Services.instance.authService.joinInvite(userName);
|
||||||
|
this.user =
|
||||||
|
await Services.instance.authService.getUser(refreshIdToken: true);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateProfile(String newUserName) async {
|
Future<void> updateProfile(String newUserName) async {
|
||||||
await Services.instance.authService.updateProfile(newUserName);
|
await Services.instance.authService.updateProfile(newUserName);
|
||||||
this.user =
|
this.user =
|
||||||
|
|||||||
@@ -1,279 +0,0 @@
|
|||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:fcs/model/buyer_model.dart';
|
|
||||||
import 'package:fcs/model/main_model.dart';
|
|
||||||
import 'package:fcs/pages/quota_page.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/util.dart';
|
|
||||||
import 'package:fcs/util.dart';
|
|
||||||
import 'package:fcs/vo/buyer.dart';
|
|
||||||
import 'package:fcs/widget/label_widgets.dart';
|
|
||||||
import 'package:fcs/widget/localization/app_translations.dart';
|
|
||||||
import 'package:fcs/widget/progress.dart';
|
|
||||||
|
|
||||||
class BuyerInfo extends StatefulWidget {
|
|
||||||
final Buyer buyer;
|
|
||||||
const BuyerInfo({this.buyer});
|
|
||||||
@override
|
|
||||||
_BuyerInfoState createState() => _BuyerInfoState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BuyerInfoState extends State<BuyerInfo> {
|
|
||||||
var dateFormatter = new DateFormat('dd MMM yyyy - hh:mm a');
|
|
||||||
TextEditingController _companyName = new TextEditingController();
|
|
||||||
TextEditingController _comAddress = new TextEditingController();
|
|
||||||
TextEditingController _numOfShops = new TextEditingController();
|
|
||||||
TextEditingController _bizType = new TextEditingController();
|
|
||||||
TextEditingController _accountName = new TextEditingController();
|
|
||||||
TextEditingController _accountNumber = new TextEditingController();
|
|
||||||
|
|
||||||
bool _isLoading = false;
|
|
||||||
Buyer buyer;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (widget.buyer != null) {
|
|
||||||
buyer = widget.buyer;
|
|
||||||
Provider.of<BuyerModel>(context, listen: false)
|
|
||||||
.loadBuyerProducts(buyer)
|
|
||||||
.then((b) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
buyer = b;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var mainModel = Provider.of<MainModel>(context);
|
|
||||||
|
|
||||||
_companyName.text = buyer.bizName;
|
|
||||||
_comAddress.text = buyer.bizAddress;
|
|
||||||
_numOfShops.text = buyer.numOfShops.toString();
|
|
||||||
_bizType.text = buyer.bizType;
|
|
||||||
_accountName.text = buyer.userName;
|
|
||||||
_accountNumber.text = buyer.userID;
|
|
||||||
|
|
||||||
final dateBox =
|
|
||||||
labeledText(context, dateFormatter.format(buyer.regDate), "reg.date");
|
|
||||||
final accountBox =
|
|
||||||
labeledText(context, buyer.userName, "buyer.account_name");
|
|
||||||
final phoneBox = labeledText(context, buyer.phone, "buyer.phone_number");
|
|
||||||
final statusBox = labeledText(context, buyer.status, "reg.status");
|
|
||||||
final bizNameBox = labeledText(context, _companyName.text, "reg.biz_name");
|
|
||||||
final bizAddressBox =
|
|
||||||
labeledText(context, _comAddress.text, "reg.biz_address");
|
|
||||||
final shopNumberBox =
|
|
||||||
labeledText(context, _numOfShops.text, "reg.biz_shops");
|
|
||||||
final typeBox = labeledText(context, _bizType.text, "buyer.type_biz");
|
|
||||||
final dailyQuotaBox = labeledText(
|
|
||||||
context, formatNumber(buyer.dailyQuota), "reg.quota",
|
|
||||||
number: true);
|
|
||||||
final dailyQuotaUsedBox = labeledText(
|
|
||||||
context, formatNumber(buyer.dailyQuotaUsed), "reg.quota.used",
|
|
||||||
number: true);
|
|
||||||
final maxQuotaBox = labeledText(
|
|
||||||
context, formatNumber(buyer.maxQuota), "reg.max_quota",
|
|
||||||
number: true);
|
|
||||||
final maxQuotaUsedBox = labeledText(
|
|
||||||
context, formatNumber(buyer.maxQuotaUsed), "reg.max_quota.used",
|
|
||||||
number: true);
|
|
||||||
final nricFrontBox =
|
|
||||||
labeledImg(context, buyer.nricFrontUrl, "reg_info.nric_front");
|
|
||||||
final nricBackBox =
|
|
||||||
labeledImg(context, buyer.nricBackUrl, "reg_info.nric_back");
|
|
||||||
|
|
||||||
return LocalProgress(
|
|
||||||
inAsyncCall: _isLoading,
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
title: Text(AppTranslations.of(context).text("buyer.title")),
|
|
||||||
actions: <Widget>[
|
|
||||||
mainModel.showHistoryBtn()
|
|
||||||
? IconButton(
|
|
||||||
icon: Icon(Icons.history),
|
|
||||||
onPressed: () {
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) =>
|
|
||||||
// DocumentLogPage(docID: buyer.id)),
|
|
||||||
// );
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
PopupMenuButton(
|
|
||||||
onSelected: (s) {
|
|
||||||
if (s == 1) {
|
|
||||||
showConfirmDialog(context, "buyer.delete.confirm", () {
|
|
||||||
_delete();
|
|
||||||
});
|
|
||||||
} else if (s == 2) {
|
|
||||||
showConfirmDialog(context, "buyer.approve.confirm", () {
|
|
||||||
_approve();
|
|
||||||
});
|
|
||||||
} else if (s == 3) {
|
|
||||||
showCommentDialog(context, (comment) {
|
|
||||||
_reject(comment);
|
|
||||||
});
|
|
||||||
} else if (s == 4) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => QuotaPage(
|
|
||||||
buyer: this.buyer,
|
|
||||||
isApproved: true,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
PopupMenuItem(
|
|
||||||
value: 1,
|
|
||||||
child: Text("Delete"),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
enabled: buyer.isPending(),
|
|
||||||
value: 2,
|
|
||||||
child: Text("Approve"),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
enabled: buyer.isPending(),
|
|
||||||
value: 3,
|
|
||||||
child: Text("Reject"),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
enabled: buyer.isApproved(),
|
|
||||||
value: 4,
|
|
||||||
child: Text("Allocate Quota"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: Container(
|
|
||||||
padding: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
|
|
||||||
child: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
dateBox,
|
|
||||||
Divider(),
|
|
||||||
accountBox,
|
|
||||||
Divider(),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: phoneBox,
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
onTap: () => call(context, buyer.phone),
|
|
||||||
child: Icon(
|
|
||||||
Icons.open_in_new,
|
|
||||||
color: Colors.grey,
|
|
||||||
size: 15,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
statusBox,
|
|
||||||
Divider(),
|
|
||||||
bizNameBox,
|
|
||||||
Divider(),
|
|
||||||
bizAddressBox,
|
|
||||||
Divider(),
|
|
||||||
typeBox,
|
|
||||||
Divider(),
|
|
||||||
dailyQuotaBox,
|
|
||||||
Divider(),
|
|
||||||
dailyQuotaUsedBox,
|
|
||||||
Divider(),
|
|
||||||
maxQuotaBox,
|
|
||||||
Divider(),
|
|
||||||
maxQuotaUsedBox,
|
|
||||||
Divider(),
|
|
||||||
nricFrontBox,
|
|
||||||
Divider(),
|
|
||||||
nricBackBox
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_delete() async {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Provider.of<BuyerModel>(context).delete(buyer);
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
} catch (e) {
|
|
||||||
showMsgDialog(context, "Error", e.toString());
|
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_approve() async {
|
|
||||||
var _buyer = await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => QuotaPage(
|
|
||||||
buyer: this.buyer,
|
|
||||||
isApproved: false,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
if (_buyer == null) return;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.buyer.dailyQuota = _buyer.dailyQuota;
|
|
||||||
this.buyer.maxQuota = _buyer.maxQuota;
|
|
||||||
await Provider.of<BuyerModel>(context).approve(this.buyer);
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
} catch (e) {
|
|
||||||
showMsgDialog(context, "Error", e.toString());
|
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_reject(comment) async {
|
|
||||||
if (comment == null || comment == "") {
|
|
||||||
showMsgDialog(context, "Error", "Please enter comment!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buyer.comment = comment;
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Provider.of<BuyerModel>(context).reject(buyer);
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
} catch (e) {
|
|
||||||
showMsgDialog(context, "Error", e.toString());
|
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
import 'package:fcs/fcs/common/pages/package/buyer_info.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:fcs/model/buyer_model.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/util.dart';
|
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
|
||||||
import 'package:fcs/vo/buyer.dart';
|
|
||||||
|
|
||||||
class BuyerListRow extends StatefulWidget {
|
|
||||||
final Buyer buyer;
|
|
||||||
const BuyerListRow({this.buyer});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_BuyerListRowState createState() => _BuyerListRowState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BuyerListRowState extends State<BuyerListRow> {
|
|
||||||
final double dotSize = 15.0;
|
|
||||||
Buyer _buyer = new Buyer();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
BuyerModel buyerModel = Provider.of<BuyerModel>(context, listen: false);
|
|
||||||
if (widget.buyer != null) {
|
|
||||||
buyerModel.buyers.forEach((b) {
|
|
||||||
if (widget.buyer.id == b.id) {
|
|
||||||
_buyer = b;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.only(left: 15, right: 15),
|
|
||||||
child: Card(
|
|
||||||
elevation: 10,
|
|
||||||
color: Colors.white,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => BuyerInfo(buyer: _buyer)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: new Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
|
||||||
child: new Row(
|
|
||||||
children: <Widget>[
|
|
||||||
new Padding(
|
|
||||||
padding: new EdgeInsets.symmetric(
|
|
||||||
horizontal: 32.0 - dotSize / 2),
|
|
||||||
child: Image.asset(
|
|
||||||
"assets/buyer.png",
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
new Expanded(
|
|
||||||
child: new Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
new Text(
|
|
||||||
_buyer.userName == null ? '' : _buyer.userName,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 15.0, color: Colors.black),
|
|
||||||
),
|
|
||||||
new Text(
|
|
||||||
_buyer.bizName == null ? "" : _buyer.bizName,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 13.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
new Text(
|
|
||||||
_buyer.phone == null ? "" : _buyer.phone,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 13.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: getStatus(_buyer.status),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:path/path.dart' as Path;
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import 'package:fcs/fcs/common/domain/constants.dart';
|
import 'package:fcs/fcs/common/domain/constants.dart';
|
||||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||||
|
import 'package:fcs/fcs/common/helpers/firebase_helper.dart';
|
||||||
import 'package:fcs/fcs/common/pages/model/base_model.dart';
|
import 'package:fcs/fcs/common/pages/model/base_model.dart';
|
||||||
import 'package:fcs/fcs/common/services/services.dart';
|
import 'package:fcs/fcs/common/services/services.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -33,12 +36,16 @@ class PackageModel extends BaseModel {
|
|||||||
} else {
|
} else {
|
||||||
path = "/$packages_collection";
|
path = "/$packages_collection";
|
||||||
}
|
}
|
||||||
|
if (listener != null) listener.cancel();
|
||||||
|
packages = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
listener = Firestore.instance
|
listener = Firestore.instance
|
||||||
.collection("$path")
|
.collection("$path")
|
||||||
.where("is_delivered", isEqualTo: false)
|
.where("is_delivered", isEqualTo: false)
|
||||||
.snapshots()
|
.snapshots()
|
||||||
.listen((QuerySnapshot snapshot) {
|
.listen((QuerySnapshot snapshot) {
|
||||||
|
print("reloaded....");
|
||||||
packages.clear();
|
packages.clear();
|
||||||
packages = snapshot.documents.map((documentSnapshot) {
|
packages = snapshot.documents.map((documentSnapshot) {
|
||||||
var package = Package.fromMap(
|
var package = Package.fromMap(
|
||||||
@@ -60,4 +67,20 @@ class PackageModel extends BaseModel {
|
|||||||
return Services.instance.packageService
|
return Services.instance.packageService
|
||||||
.createPackages(packages, user.fcsID);
|
.createPackages(packages, user.fcsID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> completeProcessing(
|
||||||
|
Package package, List<File> files, List<String> deletedUrls) async {
|
||||||
|
if (files != null) {
|
||||||
|
if (files.length > 5) throw Exception("Exceed number of file upload");
|
||||||
|
package.photoUrls = package.photoUrls == null ? [] : package.photoUrls;
|
||||||
|
for (File f in files) {
|
||||||
|
String path = Path.join(pkg_files_path, package.userID, package.id);
|
||||||
|
String url = await uploadStorage(path, f);
|
||||||
|
package.photoUrls.add(url);
|
||||||
|
}
|
||||||
|
package.photoUrls.removeWhere((e) => deletedUrls.contains(e));
|
||||||
|
}
|
||||||
|
await request("/packages", "PUT",
|
||||||
|
payload: package.toJson(), token: await getToken());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,550 +0,0 @@
|
|||||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
|
||||||
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
|
||||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/model/main_model.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/shipping_address_editor.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/shipping_address_list.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/shipping_address_row.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/util.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/widgets/fcs_expansion_tile.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/widgets/my_data_table.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
|
||||||
import 'package:fcs/pages/barcode_screen_page.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_icons/flutter_icons.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:timeline_list/timeline.dart';
|
|
||||||
import 'package:timeline_list/timeline_model.dart';
|
|
||||||
|
|
||||||
class PackageCreation extends StatefulWidget {
|
|
||||||
final Package package;
|
|
||||||
PackageCreation({this.package});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_PackageCreationState createState() => _PackageCreationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PackageCreationState extends State<PackageCreation> {
|
|
||||||
TextEditingController _addressEditingController = new TextEditingController();
|
|
||||||
TextEditingController _fromTimeEditingController =
|
|
||||||
new TextEditingController();
|
|
||||||
TextEditingController _toTimeEditingController = new TextEditingController();
|
|
||||||
TextEditingController _noOfPackageEditingController =
|
|
||||||
new TextEditingController();
|
|
||||||
TextEditingController _weightEditingController = new TextEditingController();
|
|
||||||
|
|
||||||
Package _package;
|
|
||||||
bool _isLoading = false;
|
|
||||||
List<String> _images = [
|
|
||||||
"assets/photos/1.jpg",
|
|
||||||
"assets/photos/2.jpg",
|
|
||||||
"assets/photos/3.jpg"
|
|
||||||
];
|
|
||||||
bool isNew;
|
|
||||||
ShippingAddress shippingAddress = ShippingAddress(
|
|
||||||
fullName: 'U Nyi Nyi',
|
|
||||||
addressLine1: '154-19 64th Ave.',
|
|
||||||
addressLine2: 'Flushing',
|
|
||||||
city: 'NY',
|
|
||||||
state: 'NY',
|
|
||||||
phoneNumber: '+1 (292)215-2247');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (widget.package != null) {
|
|
||||||
_package = widget.package;
|
|
||||||
isNew = false;
|
|
||||||
// _addressEditingController.text = _pickUp.address;
|
|
||||||
// _fromTimeEditingController.text = _pickUp.fromTime;
|
|
||||||
// _toTimeEditingController.text = _pickUp.toTime;
|
|
||||||
// _noOfPackageEditingController.text = _pickUp.numberOfPackage.toString();
|
|
||||||
// _weightEditingController.text = _pickUp.weight.toString();
|
|
||||||
} else {
|
|
||||||
isNew = true;
|
|
||||||
_package = Package(rate: 0, weight: 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
|
||||||
|
|
||||||
List<TimelineModel> _models() {
|
|
||||||
print('_package.statusHistory=> ${_package.shipmentHistory}');
|
|
||||||
return _package.shipmentHistory
|
|
||||||
.map((e) => TimelineModel(
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(18.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(e.status,
|
|
||||||
style: TextStyle(
|
|
||||||
color: e.done ? primaryColor : Colors.grey,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold)),
|
|
||||||
e.status == "Processed"
|
|
||||||
? Text("(Waiting for payment)",
|
|
||||||
style: TextStyle(
|
|
||||||
color: e.done ? primaryColor : Colors.grey,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold))
|
|
||||||
: Container(),
|
|
||||||
Text(dateFormat.format(e.date)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
iconBackground: e.done ? primaryColor : Colors.grey,
|
|
||||||
icon: Icon(
|
|
||||||
e.status == "Shipped"
|
|
||||||
? Ionicons.ios_airplane
|
|
||||||
: e.status == "Delivered"
|
|
||||||
? MaterialCommunityIcons.truck_fast
|
|
||||||
: e.status == "Processed"
|
|
||||||
? MaterialIcons.check
|
|
||||||
: Octicons.package,
|
|
||||||
color: Colors.white,
|
|
||||||
)))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var owner = Provider.of<MainModel>(context).user.hasPackages();
|
|
||||||
|
|
||||||
final trackingIdBox = Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 20.0, right: 20),
|
|
||||||
child: TextFormField(
|
|
||||||
initialValue: isNew ? "" : "zdf-sdfl-37sdfks",
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
labelText: 'Tracking ID',
|
|
||||||
hintText: 'Tracking ID',
|
|
||||||
filled: true,
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Ionicons.ios_barcode,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
BottomUpPageRoute(BarcodeScreenPage()),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
icon: Icon(Octicons.package, color: primaryColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
var images = isNew ? [] : _images;
|
|
||||||
return LocalProgress(
|
|
||||||
inAsyncCall: _isLoading,
|
|
||||||
child: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: true,
|
|
||||||
leading: new IconButton(
|
|
||||||
icon: new Icon(Icons.close),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
title: Text(AppTranslations.of(context).text("package.create.title")),
|
|
||||||
),
|
|
||||||
body: Card(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
isNew ? Container() : Center(child: nameWidget(_package.market)),
|
|
||||||
Expanded(
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
owner
|
|
||||||
? FcsExpansionTile(
|
|
||||||
title: Text(
|
|
||||||
'Receiving',
|
|
||||||
style: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
trackingIdBox,
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 20.0, right: 20),
|
|
||||||
child: TextFormField(
|
|
||||||
initialValue: isNew ? "" : "FCS-0203-390-2",
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
labelText: 'FCS_ID',
|
|
||||||
hintText: 'FCS_ID',
|
|
||||||
filled: true,
|
|
||||||
icon: Icon(Feather.user,
|
|
||||||
color: primaryColor),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
onPressed: () {})),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 20.0, right: 20),
|
|
||||||
child: TextFormField(
|
|
||||||
initialValue: _package.receiverName,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
labelText: 'Customer Name',
|
|
||||||
filled: true,
|
|
||||||
icon: Icon(Feather.user,
|
|
||||||
color: Colors.white),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
onPressed: () {})),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
owner
|
|
||||||
? isNew
|
|
||||||
? Container()
|
|
||||||
: ExpansionTile(
|
|
||||||
title: Text(
|
|
||||||
'Processing',
|
|
||||||
style: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 20.0, right: 20),
|
|
||||||
child: TextFormField(
|
|
||||||
initialValue: isNew
|
|
||||||
? ""
|
|
||||||
: _package.cargoDesc.toString(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor: Colors.white,
|
|
||||||
labelText: 'Description',
|
|
||||||
filled: true,
|
|
||||||
icon: Icon(MaterialIcons.description,
|
|
||||||
color: primaryColor),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 20.0, right: 20),
|
|
||||||
child: fcsInput(
|
|
||||||
"Remark",
|
|
||||||
MaterialCommunityIcons.note,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
isNew
|
|
||||||
? Container()
|
|
||||||
: ExpansionTile(
|
|
||||||
title: Text(
|
|
||||||
'Photos',
|
|
||||||
style: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
height: 130,
|
|
||||||
width: 500,
|
|
||||||
child: ListView.separated(
|
|
||||||
separatorBuilder: (context, index) => Divider(
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
itemCount: images.length + 1,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == images.length) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Container(
|
|
||||||
width: 200,
|
|
||||||
height: 70,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: primaryColor,
|
|
||||||
width: 2.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Icon(SimpleLineIcons.plus),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Container(
|
|
||||||
width: 200,
|
|
||||||
height: 70,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: primaryColor,
|
|
||||||
width: 2.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Image.asset(images[index],
|
|
||||||
width: 50, height: 50),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
isNew ? Container() : getShippingAddressList(context),
|
|
||||||
isNew
|
|
||||||
? Container()
|
|
||||||
: ExpansionTile(
|
|
||||||
title: Text(
|
|
||||||
'Status',
|
|
||||||
style: TextStyle(
|
|
||||||
color: primaryColor,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
height: 500,
|
|
||||||
padding: EdgeInsets.only(left: 20),
|
|
||||||
child: isNew
|
|
||||||
? Container()
|
|
||||||
: Timeline(
|
|
||||||
children: _models(),
|
|
||||||
position: TimelinePosition.Left),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
owner
|
|
||||||
? widget.package == null
|
|
||||||
? Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 250,
|
|
||||||
child: FlatButton(
|
|
||||||
child: Text('Complete receiving'),
|
|
||||||
color: primaryColor,
|
|
||||||
textColor: Colors.white,
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)))
|
|
||||||
: Container(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 250,
|
|
||||||
child: FlatButton(
|
|
||||||
child: Text('Complete processing'),
|
|
||||||
color: primaryColor,
|
|
||||||
textColor: Colors.white,
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
))),
|
|
||||||
],
|
|
||||||
))
|
|
||||||
: Container()
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget getShippingAddressList(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
child: ExpansionTile(
|
|
||||||
title: Text(
|
|
||||||
"Shipping Addresses",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontStyle: FontStyle.normal,
|
|
||||||
color: primaryColor),
|
|
||||||
),
|
|
||||||
children: <Widget>[
|
|
||||||
// Column(
|
|
||||||
// children: getAddressList(context, shipmentModel.shippingAddresses),
|
|
||||||
// ),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(left: 10, right: 10),
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: new Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
new Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.fullName == null
|
|
||||||
? ''
|
|
||||||
: shippingAddress.fullName,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 15.0,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.addressLine1 == null
|
|
||||||
? ''
|
|
||||||
: shippingAddress.addressLine1,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 14.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.addressLine2 == null
|
|
||||||
? ''
|
|
||||||
: shippingAddress.addressLine2,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 14.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.city == null
|
|
||||||
? ''
|
|
||||||
: shippingAddress.city,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 14.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.state == null
|
|
||||||
? ''
|
|
||||||
: shippingAddress.state,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 14.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
|
||||||
child: new Text(
|
|
||||||
shippingAddress.phoneNumber == null
|
|
||||||
? ''
|
|
||||||
: "Phone:${shippingAddress.phoneNumber}",
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 14.0, color: Colors.grey),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 20, bottom: 15, right: 15),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomRight,
|
|
||||||
child: Container(
|
|
||||||
width: 130,
|
|
||||||
height: 40,
|
|
||||||
child: FloatingActionButton.extended(
|
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
onPressed: () async {
|
|
||||||
var address = await Navigator.push(
|
|
||||||
context,
|
|
||||||
BottomUpPageRoute(ShippingAddressList()),
|
|
||||||
);
|
|
||||||
print('address => ${address}');
|
|
||||||
setState(() {
|
|
||||||
if (address != null) {
|
|
||||||
this.shippingAddress = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.add),
|
|
||||||
label: Text(
|
|
||||||
'Select \nAddress',
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> getAddressList(
|
|
||||||
BuildContext context, List<ShippingAddress> addresses) {
|
|
||||||
return addresses.asMap().entries.map((s) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
BottomUpPageRoute(ShippingAddressEditor(shippingAddress: s.value)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: ShippingAddressRow(shippingAddress: s.value, index: s.key),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<MyDataRow> getAddressRows(List<ShippingAddress> addresses) {
|
|
||||||
return addresses.map((s) {
|
|
||||||
return MyDataRow(
|
|
||||||
onSelectChanged: (selected) {},
|
|
||||||
cells: [
|
|
||||||
MyDataCell(
|
|
||||||
new Text(
|
|
||||||
s.fullName,
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MyDataCell(
|
|
||||||
new Text(
|
|
||||||
s.phoneNumber,
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
427
lib/fcs/common/pages/package/package_editor copy.dart
Normal file
427
lib/fcs/common/pages/package/package_editor copy.dart
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
import 'package:fcs/fcs/common/domain/entities/market.dart';
|
||||||
|
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||||
|
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
||||||
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
|
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/market/market_editor.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/market/model/market_model.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/model/main_model.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/shipping_address_editor.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/shipping_address_list.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/shipping_address_row.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/util.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/fcs_expansion_tile.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/image_slider.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/input_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/my_data_table.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||||
|
import 'package:fcs/pages/barcode_screen_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:timeline_list/timeline.dart';
|
||||||
|
import 'package:timeline_list/timeline_model.dart';
|
||||||
|
|
||||||
|
class PackageEditorPage extends StatefulWidget {
|
||||||
|
final Package package;
|
||||||
|
PackageEditorPage({this.package});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PackageEditorPageState createState() => _PackageEditorPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PackageEditorPageState extends State<PackageEditorPage> {
|
||||||
|
TextEditingController _remarkCtl = new TextEditingController();
|
||||||
|
TextEditingController _descCtl = new TextEditingController();
|
||||||
|
|
||||||
|
Package _package;
|
||||||
|
bool _isLoading = false;
|
||||||
|
List<String> images = [
|
||||||
|
"assets/photos/1.jpg",
|
||||||
|
"assets/photos/2.jpg",
|
||||||
|
"assets/photos/3.jpg"
|
||||||
|
];
|
||||||
|
ShippingAddress shippingAddress = ShippingAddress(
|
||||||
|
fullName: 'U Nyi Nyi',
|
||||||
|
addressLine1: '154-19 64th Ave.',
|
||||||
|
addressLine2: 'Flushing',
|
||||||
|
city: 'NY',
|
||||||
|
state: 'NY',
|
||||||
|
phoneNumber: '+1 (292)215-2247');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_package = widget.package;
|
||||||
|
selectedMarket = _package.market;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||||
|
|
||||||
|
List<TimelineModel> _models() {
|
||||||
|
if (_package.shipmentHistory == null) return [];
|
||||||
|
return _package.shipmentHistory
|
||||||
|
.map((e) => TimelineModel(
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(18.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(e.status,
|
||||||
|
style: TextStyle(
|
||||||
|
color: e.done ? primaryColor : Colors.grey,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Text(dateFormat.format(e.date)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||||
|
icon: Icon(
|
||||||
|
e.status == "shipped"
|
||||||
|
? Ionicons.ios_airplane
|
||||||
|
: e.status == "delivered"
|
||||||
|
? MaterialCommunityIcons.truck_fast
|
||||||
|
: e.status == "processed"
|
||||||
|
? MaterialIcons.check
|
||||||
|
: Octicons.package,
|
||||||
|
color: Colors.white,
|
||||||
|
)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNew = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var owner = Provider.of<MainModel>(context).user.hasPackages();
|
||||||
|
|
||||||
|
final trackingIdBox = DisplayText(
|
||||||
|
text: _package.trackingID,
|
||||||
|
labelText: getLocalString(context, "package.tracking.id"),
|
||||||
|
iconData: MaterialCommunityIcons.barcode_scan,
|
||||||
|
);
|
||||||
|
final customerNameBox = DisplayText(
|
||||||
|
text: _package.userName,
|
||||||
|
labelText: getLocalString(context, "package.create.name"),
|
||||||
|
iconData: Icons.perm_identity,
|
||||||
|
);
|
||||||
|
final completeProcessingBtn = fcsButton(
|
||||||
|
context,
|
||||||
|
getLocalString(context, 'package.edit.complete.process.btn'),
|
||||||
|
callack: _completeProcessing,
|
||||||
|
);
|
||||||
|
final descBox = fcsInput(getLocalString(context, "package.edit.desc"),
|
||||||
|
MaterialCommunityIcons.message_text_outline,
|
||||||
|
controller: _descCtl, autoFocus: true);
|
||||||
|
final remarkBox = fcsInput(
|
||||||
|
getLocalString(context, "package.edit.remark"), Entypo.new_message,
|
||||||
|
controller: _remarkCtl, autoFocus: true);
|
||||||
|
|
||||||
|
return LocalProgress(
|
||||||
|
inAsyncCall: _isLoading,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
title: LocalText(
|
||||||
|
context,
|
||||||
|
"package.edit.title",
|
||||||
|
fontSize: 20,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
trackingIdBox,
|
||||||
|
customerNameBox,
|
||||||
|
owner
|
||||||
|
? isNew
|
||||||
|
? Container()
|
||||||
|
: ExpansionTile(
|
||||||
|
title: Text(
|
||||||
|
'Processing',
|
||||||
|
style: TextStyle(
|
||||||
|
color: primaryColor, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
dropDown(),
|
||||||
|
descBox,
|
||||||
|
remarkBox,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
getImgSlider(images),
|
||||||
|
ExpansionTile(
|
||||||
|
title: Text(
|
||||||
|
'Status',
|
||||||
|
style:
|
||||||
|
TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(left: 20),
|
||||||
|
height: 400,
|
||||||
|
child: Timeline(
|
||||||
|
children: _models(), position: TimelinePosition.Left),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
completeProcessingBtn,
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String selectedMarket;
|
||||||
|
Widget dropDown() {
|
||||||
|
List<Market> _markets = Provider.of<MarketModel>(context).markets;
|
||||||
|
List<String> markets = _markets.map((e) => e.name).toList();
|
||||||
|
markets.insert(0, MANAGE_MARKET);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 18.0),
|
||||||
|
child: LocalText(
|
||||||
|
context,
|
||||||
|
"package.create.market",
|
||||||
|
color: primaryColor,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 150,
|
||||||
|
child: DropdownButton<String>(
|
||||||
|
value: selectedMarket,
|
||||||
|
style: TextStyle(color: Colors.black, fontSize: 14),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onChanged: (String newValue) {
|
||||||
|
setState(() {
|
||||||
|
if (newValue == MANAGE_MARKET) {
|
||||||
|
selectedMarket = null;
|
||||||
|
_manageMarket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedMarket = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isExpanded: true,
|
||||||
|
items: markets.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: value == MANAGE_MARKET
|
||||||
|
? secondaryColor
|
||||||
|
: primaryColor)),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_manageMarket() {
|
||||||
|
Navigator.push<Package>(
|
||||||
|
context,
|
||||||
|
BottomUpPageRoute(MarketEditor()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getShippingAddressList(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text(
|
||||||
|
"Shipping Addresses",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontStyle: FontStyle.normal,
|
||||||
|
color: primaryColor),
|
||||||
|
),
|
||||||
|
children: <Widget>[
|
||||||
|
// Column(
|
||||||
|
// children: getAddressList(context, shipmentModel.shippingAddresses),
|
||||||
|
// ),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(left: 10, right: 10),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: new Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
new Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.fullName == null
|
||||||
|
? ''
|
||||||
|
: shippingAddress.fullName,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 15.0,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.addressLine1 == null
|
||||||
|
? ''
|
||||||
|
: shippingAddress.addressLine1,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 14.0, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.addressLine2 == null
|
||||||
|
? ''
|
||||||
|
: shippingAddress.addressLine2,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 14.0, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.city == null
|
||||||
|
? ''
|
||||||
|
: shippingAddress.city,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 14.0, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.state == null
|
||||||
|
? ''
|
||||||
|
: shippingAddress.state,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 14.0, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: new Text(
|
||||||
|
shippingAddress.phoneNumber == null
|
||||||
|
? ''
|
||||||
|
: "Phone:${shippingAddress.phoneNumber}",
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 14.0, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(top: 20, bottom: 15, right: 15),
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Container(
|
||||||
|
width: 130,
|
||||||
|
height: 40,
|
||||||
|
child: FloatingActionButton.extended(
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
onPressed: () async {
|
||||||
|
var address = await Navigator.push(
|
||||||
|
context,
|
||||||
|
BottomUpPageRoute(ShippingAddressList()),
|
||||||
|
);
|
||||||
|
print('address => ${address}');
|
||||||
|
setState(() {
|
||||||
|
if (address != null) {
|
||||||
|
this.shippingAddress = address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.add),
|
||||||
|
label: Text(
|
||||||
|
'Select \nAddress',
|
||||||
|
style: TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> getAddressList(
|
||||||
|
BuildContext context, List<ShippingAddress> addresses) {
|
||||||
|
return addresses.asMap().entries.map((s) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
BottomUpPageRoute(ShippingAddressEditor(shippingAddress: s.value)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: ShippingAddressRow(shippingAddress: s.value, index: s.key),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MyDataRow> getAddressRows(List<ShippingAddress> addresses) {
|
||||||
|
return addresses.map((s) {
|
||||||
|
return MyDataRow(
|
||||||
|
onSelectChanged: (selected) {},
|
||||||
|
cells: [
|
||||||
|
MyDataCell(
|
||||||
|
new Text(
|
||||||
|
s.fullName,
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MyDataCell(
|
||||||
|
new Text(
|
||||||
|
s.phoneNumber,
|
||||||
|
style: textStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
_completeProcessing() {}
|
||||||
|
}
|
||||||
271
lib/fcs/common/pages/package/package_editor.dart
Normal file
271
lib/fcs/common/pages/package/package_editor.dart
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
import 'package:fcs/fcs/common/domain/entities/market.dart';
|
||||||
|
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||||
|
import 'package:fcs/fcs/common/domain/vo/shipping_address.dart';
|
||||||
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/market/market_editor.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/market/model/market_model.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/util.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/image_slider.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/multi_img_controller.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/multi_img_file.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:timeline_list/timeline_model.dart';
|
||||||
|
|
||||||
|
class PackageEditorPage extends StatefulWidget {
|
||||||
|
final Package package;
|
||||||
|
PackageEditorPage({this.package});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PackageEditorPageState createState() => _PackageEditorPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PackageEditorPageState extends State<PackageEditorPage> {
|
||||||
|
TextEditingController _remarkCtl = new TextEditingController();
|
||||||
|
TextEditingController _descCtl = new TextEditingController();
|
||||||
|
|
||||||
|
Package _package;
|
||||||
|
bool _isLoading = false;
|
||||||
|
ShippingAddress shippingAddress = ShippingAddress(
|
||||||
|
fullName: 'U Nyi Nyi',
|
||||||
|
addressLine1: '154-19 64th Ave.',
|
||||||
|
addressLine2: 'Flushing',
|
||||||
|
city: 'NY',
|
||||||
|
state: 'NY',
|
||||||
|
phoneNumber: '+1 (292)215-2247');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_package = widget.package;
|
||||||
|
selectedMarket = _package.market;
|
||||||
|
_descCtl.text = _package.desc;
|
||||||
|
_remarkCtl.text = _package.remark;
|
||||||
|
multiImgController.setImageUrls = _package.photoUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||||
|
|
||||||
|
List<TimelineModel> _models() {
|
||||||
|
if (_package.shipmentHistory == null) return [];
|
||||||
|
return _package.shipmentHistory
|
||||||
|
.map((e) => TimelineModel(
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(18.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(e.status,
|
||||||
|
style: TextStyle(
|
||||||
|
color: e.done ? primaryColor : Colors.grey,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Text(dateFormat.format(e.date)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||||
|
icon: Icon(
|
||||||
|
e.status == "shipped"
|
||||||
|
? Ionicons.ios_airplane
|
||||||
|
: e.status == "delivered"
|
||||||
|
? MaterialCommunityIcons.truck_fast
|
||||||
|
: e.status == "processed"
|
||||||
|
? MaterialIcons.check
|
||||||
|
: Octicons.package,
|
||||||
|
color: Colors.white,
|
||||||
|
)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNew = false;
|
||||||
|
MultiImgController multiImgController = MultiImgController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final trackingIdBox = DisplayText(
|
||||||
|
text: _package.trackingID,
|
||||||
|
labelText: getLocalString(context, "package.tracking.id"),
|
||||||
|
iconData: MaterialCommunityIcons.barcode_scan,
|
||||||
|
);
|
||||||
|
final statusBox = DisplayText(
|
||||||
|
text: _package.currentStatus,
|
||||||
|
labelText: getLocalString(context, "package.edit.status"),
|
||||||
|
iconData: AntDesign.exclamationcircleo,
|
||||||
|
);
|
||||||
|
final customerNameBox = DisplayText(
|
||||||
|
text: _package.userName,
|
||||||
|
labelText: getLocalString(context, "package.create.name"),
|
||||||
|
iconData: Icons.perm_identity,
|
||||||
|
);
|
||||||
|
final completeProcessingBtn = fcsButton(
|
||||||
|
context,
|
||||||
|
getLocalString(context, 'package.edit.complete.process.btn'),
|
||||||
|
callack: _completeProcessing,
|
||||||
|
);
|
||||||
|
final descBox = fcsInput(getLocalString(context, "package.edit.desc"),
|
||||||
|
MaterialCommunityIcons.message_text_outline,
|
||||||
|
controller: _descCtl, autoFocus: false);
|
||||||
|
final remarkBox = fcsInput(
|
||||||
|
getLocalString(context, "package.edit.remark"), Entypo.new_message,
|
||||||
|
controller: _remarkCtl, autoFocus: false);
|
||||||
|
final img = MultiImageFile(
|
||||||
|
enabled: true,
|
||||||
|
controller: multiImgController,
|
||||||
|
title: "Receipt File",
|
||||||
|
);
|
||||||
|
return LocalProgress(
|
||||||
|
inAsyncCall: _isLoading,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
leading: new IconButton(
|
||||||
|
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
title: LocalText(
|
||||||
|
context,
|
||||||
|
"package.edit.title",
|
||||||
|
fontSize: 20,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
trackingIdBox,
|
||||||
|
customerNameBox,
|
||||||
|
statusBox,
|
||||||
|
Divider(),
|
||||||
|
Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: LocalText(
|
||||||
|
context,
|
||||||
|
"package.edit.procseeing",
|
||||||
|
color: primaryColor,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 18.0, right: 10),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
marketDropdown(),
|
||||||
|
descBox,
|
||||||
|
remarkBox,
|
||||||
|
img,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
completeProcessingBtn,
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String selectedMarket;
|
||||||
|
Widget marketDropdown() {
|
||||||
|
List<Market> _markets = Provider.of<MarketModel>(context).markets;
|
||||||
|
List<String> markets = _markets.map((e) => e.name).toList();
|
||||||
|
markets.insert(0, MANAGE_MARKET);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 18.0),
|
||||||
|
child: LocalText(
|
||||||
|
context,
|
||||||
|
"package.create.market",
|
||||||
|
color: primaryColor,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 150,
|
||||||
|
child: DropdownButton<String>(
|
||||||
|
value: selectedMarket,
|
||||||
|
style: TextStyle(color: Colors.black, fontSize: 14),
|
||||||
|
underline: Container(
|
||||||
|
height: 1,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onChanged: (String newValue) {
|
||||||
|
setState(() {
|
||||||
|
if (newValue == MANAGE_MARKET) {
|
||||||
|
selectedMarket = null;
|
||||||
|
_manageMarket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedMarket = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isExpanded: true,
|
||||||
|
items: markets.map<DropdownMenuItem<String>>((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: value == MANAGE_MARKET
|
||||||
|
? secondaryColor
|
||||||
|
: primaryColor)),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_manageMarket() {
|
||||||
|
Navigator.push<Package>(
|
||||||
|
context,
|
||||||
|
BottomUpPageRoute(MarketEditor()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_completeProcessing() async {
|
||||||
|
if (_descCtl.text == "") {
|
||||||
|
showMsgDialog(context, "Error", "Expected some description");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
PackageModel packageModel =
|
||||||
|
Provider.of<PackageModel>(context, listen: false);
|
||||||
|
try {
|
||||||
|
_package.desc = _descCtl.text;
|
||||||
|
_package.remark = _remarkCtl.text;
|
||||||
|
_package.market = selectedMarket;
|
||||||
|
await packageModel.completeProcessing(_package,
|
||||||
|
multiImgController.getAddedFile, multiImgController.getDeletedUrl);
|
||||||
|
Navigator.pop(context);
|
||||||
|
} catch (e) {
|
||||||
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
import 'package:fcs/fcs/common/pages/package/package_editor.dart';
|
||||||
import 'package:fcs/fcs/common/pages/widgets/label_widgets.dart';
|
import 'package:fcs/fcs/common/pages/util.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/display_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/local_text.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/multi_img_controller.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/multi_img_file.dart';
|
||||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:timeline_list/timeline.dart';
|
||||||
|
import 'package:timeline_list/timeline_model.dart';
|
||||||
|
|
||||||
|
final DateFormat dateFormat = DateFormat("d MMM yyyy");
|
||||||
|
|
||||||
class PackageInfo extends StatefulWidget {
|
class PackageInfo extends StatefulWidget {
|
||||||
final Package package;
|
final Package package;
|
||||||
@@ -19,13 +28,13 @@ class _PackageInfoState extends State<PackageInfo> {
|
|||||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||||
Package _package;
|
Package _package;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
MultiImgController multiImgController = MultiImgController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (widget.package != null) {
|
|
||||||
_package = widget.package;
|
_package = widget.package;
|
||||||
}
|
multiImgController.setImageUrls = _package.photoUrls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -35,17 +44,70 @@ class _PackageInfoState extends State<PackageInfo> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final trackingIdBox = DisplayText(
|
||||||
|
text: _package.trackingID,
|
||||||
|
labelText: getLocalString(context, "package.tracking.id"),
|
||||||
|
iconData: MaterialCommunityIcons.barcode_scan,
|
||||||
|
);
|
||||||
|
final customerNameBox = DisplayText(
|
||||||
|
text: _package.userName,
|
||||||
|
labelText: getLocalString(context, "package.create.name"),
|
||||||
|
iconData: Icons.perm_identity,
|
||||||
|
);
|
||||||
|
final statusBox = DisplayText(
|
||||||
|
text: _package.currentStatus,
|
||||||
|
labelText: getLocalString(context, "package.edit.status"),
|
||||||
|
iconData: AntDesign.exclamationcircleo,
|
||||||
|
);
|
||||||
|
final marketBox = DisplayText(
|
||||||
|
text: _package.market ?? "-",
|
||||||
|
labelText: getLocalString(context, "package.create.market"),
|
||||||
|
iconData: Icons.store,
|
||||||
|
);
|
||||||
|
final descBox = DisplayText(
|
||||||
|
text: _package.desc ?? "-",
|
||||||
|
labelText: getLocalString(context, "package.edit.desc"),
|
||||||
|
iconData: MaterialCommunityIcons.message_text_outline,
|
||||||
|
);
|
||||||
|
final remarkBox = DisplayText(
|
||||||
|
text: _package.remark ?? "-",
|
||||||
|
labelText: getLocalString(context, "package.edit.remark"),
|
||||||
|
iconData: Entypo.new_message,
|
||||||
|
);
|
||||||
|
final img = MultiImageFile(
|
||||||
|
enabled: false,
|
||||||
|
controller: multiImgController,
|
||||||
|
title: "Receipt File",
|
||||||
|
);
|
||||||
|
|
||||||
return LocalProgress(
|
return LocalProgress(
|
||||||
inAsyncCall: _isLoading,
|
inAsyncCall: _isLoading,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: new IconButton(
|
leading: new IconButton(
|
||||||
icon: new Icon(Icons.close),
|
icon: new Icon(Icons.close, color: primaryColor, size: 30),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
),
|
),
|
||||||
backgroundColor: primaryColor,
|
shadowColor: Colors.transparent,
|
||||||
title: Text(AppTranslations.of(context).text("package.edit.title")),
|
backgroundColor: Colors.white,
|
||||||
|
title: LocalText(
|
||||||
|
context,
|
||||||
|
"package.info.title",
|
||||||
|
fontSize: 20,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.edit, color: primaryColor),
|
||||||
|
onPressed: () => Navigator.push<Package>(
|
||||||
|
context,
|
||||||
|
BottomUpPageRoute(PackageEditorPage(
|
||||||
|
package: widget.package,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Card(
|
body: Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -54,80 +116,32 @@ class _PackageInfoState extends State<PackageInfo> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: ListView(children: <Widget>[
|
child: ListView(children: <Widget>[
|
||||||
Container(
|
trackingIdBox,
|
||||||
padding: EdgeInsets.only(top: 10),
|
customerNameBox,
|
||||||
child: Row(
|
marketBox,
|
||||||
children: <Widget>[
|
statusBox,
|
||||||
Icon(
|
_package.photoUrls.length == 0 ? Container() : img,
|
||||||
Icons.calendar_today,
|
descBox,
|
||||||
|
remarkBox,
|
||||||
|
ExpansionTile(
|
||||||
|
initiallyExpanded: true,
|
||||||
|
title: Text(
|
||||||
|
'Status',
|
||||||
|
style: TextStyle(
|
||||||
|
color: primaryColor, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Padding(
|
children: <Widget>[
|
||||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
Container(
|
||||||
child: labeledText(
|
padding: EdgeInsets.only(left: 20),
|
||||||
context,
|
height: 400,
|
||||||
dateFormatter.format(_package.arrivedDate),
|
child: Timeline(
|
||||||
"package.arrival.date"),
|
children: _models(),
|
||||||
),
|
position: TimelinePosition.Left),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Icon(Icons.pages),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
|
||||||
child: labeledText(context, _package.packageNumber,
|
|
||||||
"package.number"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Icon(FontAwesomeIcons.weightHanging),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
|
||||||
child: labeledText(
|
|
||||||
context,
|
|
||||||
"${_package.weight.toString()} lb",
|
|
||||||
"package.weight"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Icon(FontAwesomeIcons.tag),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
|
||||||
child: labeledText(context, _package.rate.toString(),
|
|
||||||
"package.rate"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 10),
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Icon(FontAwesomeIcons.moneyBill),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0, left: 15),
|
|
||||||
child: labeledText(
|
|
||||||
context,
|
|
||||||
_package.price == null
|
|
||||||
? ""
|
|
||||||
: "\$ " + _package.price.toString(),
|
|
||||||
"package.amount"),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
@@ -137,4 +151,36 @@ class _PackageInfoState extends State<PackageInfo> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<TimelineModel> _models() {
|
||||||
|
if (_package.shipmentHistory == null) return [];
|
||||||
|
return _package.shipmentHistory
|
||||||
|
.map((e) => TimelineModel(
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(18.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(e.status,
|
||||||
|
style: TextStyle(
|
||||||
|
color: e.done ? primaryColor : Colors.grey,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
Text(dateFormat.format(e.date)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
iconBackground: e.done ? primaryColor : Colors.grey,
|
||||||
|
icon: Icon(
|
||||||
|
e.status == "shipped"
|
||||||
|
? Ionicons.ios_airplane
|
||||||
|
: e.status == "delivered"
|
||||||
|
? MaterialCommunityIcons.truck_fast
|
||||||
|
: e.status == "processed"
|
||||||
|
? MaterialIcons.check
|
||||||
|
: Octicons.package,
|
||||||
|
color: Colors.white,
|
||||||
|
)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
import 'package:fcs/fcs/common/localization/app_translations.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/package_creation.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/package_list_row.dart';
|
import 'package:fcs/fcs/common/pages/package/package_list_row.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/package_new.dart';
|
import 'package:fcs/fcs/common/pages/package/package_new.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/search_page.dart';
|
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
import 'package:fcs/fcs/common/pages/widgets/progress.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -70,7 +68,7 @@ class _PackageListState extends State<PackageList> {
|
|||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_newPickup();
|
_newPackage();
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.add),
|
icon: Icon(Icons.add),
|
||||||
label:
|
label:
|
||||||
@@ -88,13 +86,12 @@ class _PackageListState extends State<PackageList> {
|
|||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return PackageListRow(
|
return PackageListRow(
|
||||||
package: packageModel.packages[index],
|
package: packageModel.packages[index],
|
||||||
isReadOnly: false,
|
|
||||||
);
|
);
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_newPickup() {
|
_newPackage() {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
BottomUpPageRoute(PackageNew()),
|
BottomUpPageRoute(PackageNew()),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
import 'package:fcs/fcs/common/domain/entities/package.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/package_creation.dart';
|
import 'package:fcs/fcs/common/pages/package/package_editor.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/package_info.dart';
|
import 'package:fcs/fcs/common/pages/package/package_info.dart';
|
||||||
import 'package:fcs/fcs/common/pages/util.dart';
|
import 'package:fcs/fcs/common/pages/util.dart';
|
||||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
@@ -7,9 +7,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class PackageListRow extends StatefulWidget {
|
class PackageListRow extends StatefulWidget {
|
||||||
final bool isReadOnly;
|
|
||||||
final Package package;
|
final Package package;
|
||||||
const PackageListRow({this.package, this.isReadOnly});
|
const PackageListRow({this.package});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PackageListRowtate createState() => _PackageListRowtate();
|
_PackageListRowtate createState() => _PackageListRowtate();
|
||||||
@@ -32,17 +31,10 @@ class _PackageListRowtate extends State<PackageListRow> {
|
|||||||
padding: EdgeInsets.only(left: 15, right: 15),
|
padding: EdgeInsets.only(left: 15, right: 15),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.isReadOnly) {
|
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
BottomUpPageRoute(PackageInfo(package: _package)),
|
BottomUpPageRoute(PackageInfo(package: _package)),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
BottomUpPageRoute(PackageCreation(package: _package)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:fcs/fcs/common/domain/entities/package.dart';
|
|||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
import 'package:fcs/fcs/common/pages/package/tracking_id_page.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||||
import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart';
|
import 'package:fcs/fcs/common/pages/staff/model/staff_model.dart';
|
||||||
import 'package:fcs/fcs/common/pages/util.dart';
|
import 'package:fcs/fcs/common/pages/util.dart';
|
||||||
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
|
||||||
import 'package:fcs/fcs/common/pages/package/user_list_row.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
|
||||||
|
|
||||||
Future<User> searchUser1(BuildContext context) async => await showSearch<User>(
|
|
||||||
context: context,
|
|
||||||
delegate: UserSearchDelegate(),
|
|
||||||
);
|
|
||||||
|
|
||||||
class UserSearchDelegate extends SearchDelegate<User> {
|
|
||||||
@override
|
|
||||||
ThemeData appBarTheme(BuildContext context) {
|
|
||||||
final ThemeData theme = Theme.of(context);
|
|
||||||
return theme.copyWith(
|
|
||||||
inputDecorationTheme: InputDecorationTheme(
|
|
||||||
hintStyle: TextStyle(
|
|
||||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
|
||||||
primaryColor: primaryColor,
|
|
||||||
primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.white),
|
|
||||||
primaryColorBrightness: Brightness.light,
|
|
||||||
primaryTextTheme: theme.textTheme,
|
|
||||||
textTheme: theme.textTheme.copyWith(
|
|
||||||
title: theme.textTheme.title.copyWith(
|
|
||||||
color: theme.primaryTextTheme.title.color, fontSize: 16)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget> buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.clear),
|
|
||||||
onPressed: () => query = '',
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: Icon(Icons.arrow_back),
|
|
||||||
onPressed: () => close(context, null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
final buyerModel = Provider.of<PackageModel>(context);
|
|
||||||
return FutureBuilder(
|
|
||||||
future: buyerModel.searchUser(query),
|
|
||||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
if (snapshot.data.length == 0) {
|
|
||||||
return Container(
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
"Error :No Search Buyer",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.only(top: 15),
|
|
||||||
child: ListView(
|
|
||||||
children:
|
|
||||||
snapshot.data.map((u) => UserListRow(user: u)).toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (snapshot.hasError) {
|
|
||||||
return Container(
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
'${snapshot.error}',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Container(
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
valueColor:
|
|
||||||
new AlwaysStoppedAnimation<Color>(primaryColor)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
child: Center(
|
|
||||||
child: Opacity(
|
|
||||||
opacity: 0.2,
|
|
||||||
child: Icon(
|
|
||||||
Icons.supervised_user_circle,
|
|
||||||
size: 200,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -113,7 +113,7 @@ class _SignupPageState extends State<SignupPage> {
|
|||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await context.read<MainModel>().signup(nameCtl.text);
|
await context.read<MainModel>().joinInvite(nameCtl.text);
|
||||||
Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false);
|
Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showMsgDialog(context, "Error", e.toString());
|
showMsgDialog(context, "Error", e.toString());
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:logging/logging.dart';
|
|||||||
class StaffModel extends BaseModel {
|
class StaffModel extends BaseModel {
|
||||||
final log = Logger('StaffModel');
|
final log = Logger('StaffModel');
|
||||||
StreamSubscription<QuerySnapshot> listener;
|
StreamSubscription<QuerySnapshot> listener;
|
||||||
|
StreamSubscription<QuerySnapshot> privilegeListener;
|
||||||
|
|
||||||
List<User> employees = [];
|
List<User> employees = [];
|
||||||
List<Privilege> privileges = [];
|
List<Privilege> privileges = [];
|
||||||
@@ -25,7 +26,9 @@ class StaffModel extends BaseModel {
|
|||||||
@override
|
@override
|
||||||
logout() async {
|
logout() async {
|
||||||
if (listener != null) listener.cancel();
|
if (listener != null) listener.cancel();
|
||||||
|
if (privilegeListener != null) privilegeListener.cancel();
|
||||||
employees = [];
|
employees = [];
|
||||||
|
privileges = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadEmployees() async {
|
Future<void> _loadEmployees() async {
|
||||||
@@ -57,7 +60,7 @@ class StaffModel extends BaseModel {
|
|||||||
if (user == null || !user.hasStaffs()) return;
|
if (user == null || !user.hasStaffs()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Firestore.instance
|
privilegeListener = Firestore.instance
|
||||||
.collection("/$privilege_collection")
|
.collection("/$privilege_collection")
|
||||||
.snapshots()
|
.snapshots()
|
||||||
.listen((QuerySnapshot snapshot) {
|
.listen((QuerySnapshot snapshot) {
|
||||||
@@ -68,8 +71,6 @@ class StaffModel extends BaseModel {
|
|||||||
return privilege;
|
return privilege;
|
||||||
}).toList();
|
}).toList();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}).onError((e) {
|
|
||||||
log.warning("Error! $e");
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warning("Error!! $e");
|
log.warning("Error!! $e");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/user_serach.dart';
|
import 'package:fcs/fcs/common/pages/user_search/user_serach.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class UserListRow extends StatefulWidget {
|
class UserListRow extends StatefulWidget {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
import 'package:fcs/fcs/common/domain/entities/user.dart';
|
||||||
import 'package:fcs/fcs/common/helpers/theme.dart';
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
import 'package:fcs/fcs/common/pages/package/model/package_model.dart';
|
||||||
import 'package:fcs/fcs/common/pages/package/user_list_row.dart';
|
import 'package:fcs/fcs/common/pages/user_search/user_list_row.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
29
lib/fcs/common/pages/widgets/display_image_source.dart
Normal file
29
lib/fcs/common/pages/widgets/display_image_source.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class DisplayImageSource {
|
||||||
|
String url;
|
||||||
|
File file;
|
||||||
|
DisplayImageSource({this.url, this.file});
|
||||||
|
|
||||||
|
ImageProvider get imageProvider =>
|
||||||
|
file == null ? NetworkImage(url) : FileImage(file);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) {
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (other.file == this.file &&
|
||||||
|
(other.file != null || this.file != null)) ||
|
||||||
|
(other.url == this.url && (other.url != null || this.url != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
int result = 17;
|
||||||
|
result = 37 * result + file.hashCode;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
lib/fcs/common/pages/widgets/image_slider.dart
Normal file
53
lib/fcs/common/pages/widgets/image_slider.dart
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
|
||||||
|
getImgSlider(List<String> images) {
|
||||||
|
return Container(
|
||||||
|
height: 130,
|
||||||
|
width: 500,
|
||||||
|
child: ListView.separated(
|
||||||
|
separatorBuilder: (context, index) => Divider(
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
itemCount: images.length + 1,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == images.length) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
height: 70,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: primaryColor,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Icon(SimpleLineIcons.plus),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
height: 70,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: primaryColor,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Image.network(
|
||||||
|
"https://lh3.googleusercontent.com/Fu9J7YpHnHK8QPr3kdAyEbTFyvB3h9Na69-j8CpQqWbMQP9sGplj7hVqQ5beKKLGgdyA8f5zIfqWdp2ITxuqlGkWDVuTyAtj_Rmw=w0",
|
||||||
|
width: 50,
|
||||||
|
height: 50),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
67
lib/fcs/common/pages/widgets/multi_img_controller.dart
Normal file
67
lib/fcs/common/pages/widgets/multi_img_controller.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'display_image_source.dart';
|
||||||
|
|
||||||
|
typedef CallBack = void Function();
|
||||||
|
|
||||||
|
class MultiImgController {
|
||||||
|
List<String> imageUrls;
|
||||||
|
List<DisplayImageSource> addedFiles = [];
|
||||||
|
List<DisplayImageSource> removedFiles = [];
|
||||||
|
|
||||||
|
List<DisplayImageSource> fileContainers = [];
|
||||||
|
CallBack callback;
|
||||||
|
MultiImgController() {
|
||||||
|
fileContainers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
set setImageUrls(List<String> imageUrls) {
|
||||||
|
if (imageUrls == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileContainers.clear();
|
||||||
|
|
||||||
|
this.imageUrls = imageUrls;
|
||||||
|
imageUrls.forEach((e) {
|
||||||
|
fileContainers.add(DisplayImageSource(url: e));
|
||||||
|
});
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChange(CallBack callBack) {
|
||||||
|
this.callback = callBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
set addFile(DisplayImageSource fileContainer) {
|
||||||
|
// if (fileContainers.contains(fileContainer)) return;
|
||||||
|
addedFiles.add(fileContainer);
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set removeFile(DisplayImageSource fileContainer) {
|
||||||
|
if (!fileContainers.contains(fileContainer)) return;
|
||||||
|
fileContainers.remove(fileContainer);
|
||||||
|
|
||||||
|
if (addedFiles.contains(fileContainer)) {
|
||||||
|
addedFiles.remove(fileContainer);
|
||||||
|
}
|
||||||
|
if (imageUrls.contains(fileContainer.url)) {
|
||||||
|
removedFiles.add(fileContainer);
|
||||||
|
}
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<File> get getAddedFile {
|
||||||
|
return addedFiles.map((e) => e.file).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> get getDeletedUrl {
|
||||||
|
return removedFiles.map((e) => e.url).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
266
lib/fcs/common/pages/widgets/multi_img_file.dart
Normal file
266
lib/fcs/common/pages/widgets/multi_img_file.dart
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/show_img.dart';
|
||||||
|
import 'package:fcs/fcs/common/pages/widgets/show_multiple_img.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_icons/flutter_icons.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
|
import 'display_image_source.dart';
|
||||||
|
import 'multi_img_controller.dart';
|
||||||
|
|
||||||
|
typedef OnFile = void Function(File);
|
||||||
|
|
||||||
|
class MultiImageFile extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final bool enabled;
|
||||||
|
final ImageSource imageSource;
|
||||||
|
final MultiImgController controller;
|
||||||
|
|
||||||
|
const MultiImageFile(
|
||||||
|
{Key key,
|
||||||
|
this.title,
|
||||||
|
this.enabled = true,
|
||||||
|
this.controller,
|
||||||
|
this.imageSource = ImageSource.gallery})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
_MultiImageFileState createState() => _MultiImageFileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MultiImageFileState extends State<MultiImageFile> {
|
||||||
|
List<DisplayImageSource> fileContainers = [];
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
fileContainers = widget.controller.fileContainers;
|
||||||
|
widget.controller.onChange(() {
|
||||||
|
setState(() {
|
||||||
|
this.fileContainers = widget.controller.fileContainers;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 130,
|
||||||
|
width: 500,
|
||||||
|
child: ListView.separated(
|
||||||
|
separatorBuilder: (context, index) => Divider(
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
itemCount:
|
||||||
|
widget.enabled ? fileContainers.length + 1 : fileContainers.length,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == fileContainers.length) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
bool camera = false, gallery = false;
|
||||||
|
await _dialog(
|
||||||
|
context, () => camera = true, () => gallery = true);
|
||||||
|
if (camera || gallery) {
|
||||||
|
var selectedFile = await ImagePicker().getImage(
|
||||||
|
source: camera ? ImageSource.camera : ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1000);
|
||||||
|
if (selectedFile != null) {
|
||||||
|
_fileAdded(DisplayImageSource(), File(selectedFile.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
height: 130,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: primaryColor,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Icon(SimpleLineIcons.plus),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShowMultiImage(
|
||||||
|
displayImageSources: fileContainers,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
child: Stack(alignment: Alignment.topLeft, children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
height: 130,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: primaryColor,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: fileContainers[index].file == null
|
||||||
|
? Image.network(fileContainers[index].url,
|
||||||
|
width: 50, height: 50)
|
||||||
|
: Image.file(fileContainers[index].file,
|
||||||
|
width: 50, height: 50),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
widget.enabled
|
||||||
|
? Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
{_fileRemove(fileContainers[index])}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileAdded(DisplayImageSource fileContainer, File selectedFile) {
|
||||||
|
fileContainer.file = selectedFile;
|
||||||
|
setState(() {
|
||||||
|
fileContainers.add(fileContainer);
|
||||||
|
widget.controller.addFile = fileContainer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileRemove(DisplayImageSource fileContainer) {
|
||||||
|
setState(() {
|
||||||
|
widget.controller.removeFile = fileContainer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getFile(DisplayImageSource fileContainer, int index) {
|
||||||
|
return Container(
|
||||||
|
height: 40,
|
||||||
|
padding: EdgeInsets.only(top: 5, bottom: 5),
|
||||||
|
child: fileContainer.file == null && fileContainer.url == null
|
||||||
|
? IconButton(
|
||||||
|
padding: const EdgeInsets.all(3.0),
|
||||||
|
icon: Icon(Icons.attach_file),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!widget.enabled) return;
|
||||||
|
bool camera = false, gallery = false;
|
||||||
|
await _dialog(
|
||||||
|
context, () => camera = true, () => gallery = true);
|
||||||
|
if (camera || gallery) {
|
||||||
|
var selectedFile = await ImagePicker().getImage(
|
||||||
|
source: camera ? ImageSource.camera : ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1000);
|
||||||
|
if (selectedFile != null) {
|
||||||
|
_fileAdded(fileContainer,
|
||||||
|
File.fromRawPath(await selectedFile.readAsBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 3.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ShowImage(
|
||||||
|
imageFile: fileContainer.file,
|
||||||
|
url: fileContainer.file == null
|
||||||
|
? fileContainer.url
|
||||||
|
: null,
|
||||||
|
fileName: widget.title)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
child: Chip(
|
||||||
|
avatar: Icon(Icons.image),
|
||||||
|
onDeleted: !widget.enabled
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
_fileRemove(fileContainer);
|
||||||
|
},
|
||||||
|
deleteIcon: Icon(
|
||||||
|
Icons.close,
|
||||||
|
),
|
||||||
|
label: Text(widget.title + " - ${index + 1}"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _dialog(BuildContext context, cameraPress(), photoPress()) {
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: Container(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
FontAwesomeIcons.camera,
|
||||||
|
size: 30,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
cameraPress();
|
||||||
|
}),
|
||||||
|
Text("Camera")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.photo_library,
|
||||||
|
size: 30,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
photoPress();
|
||||||
|
}),
|
||||||
|
Text("Gallery")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/fcs/common/pages/widgets/show_img.dart
Normal file
35
lib/fcs/common/pages/widgets/show_img.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fcs/fcs/common/helpers/theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
|
||||||
|
class ShowImage extends StatefulWidget {
|
||||||
|
final String url;
|
||||||
|
final File imageFile;
|
||||||
|
final String fileName;
|
||||||
|
const ShowImage({Key key, this.imageFile, this.fileName, this.url})
|
||||||
|
: super(key: key);
|
||||||
|
@override
|
||||||
|
_ShowImageState createState() => _ShowImageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShowImageState extends State<ShowImage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: primaryColor,
|
||||||
|
title: Text(widget.fileName),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: widget.url != null || widget.imageFile != null
|
||||||
|
? PhotoView(
|
||||||
|
imageProvider: widget.url != null
|
||||||
|
? NetworkImage(widget.url)
|
||||||
|
: FileImage(widget.imageFile),
|
||||||
|
minScale: PhotoViewComputedScale.contained * 1)
|
||||||
|
: Container()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
lib/fcs/common/pages/widgets/show_multiple_img.dart
Normal file
42
lib/fcs/common/pages/widgets/show_multiple_img.dart
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:fcs/fcs/common/pages/widgets/display_image_source.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
|
|
||||||
|
class ShowMultiImage extends StatefulWidget {
|
||||||
|
final List<DisplayImageSource> displayImageSources;
|
||||||
|
const ShowMultiImage({Key key, this.displayImageSources}) : super(key: key);
|
||||||
|
@override
|
||||||
|
_ShowMultiImageState createState() => _ShowMultiImageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ShowMultiImageState extends State<ShowMultiImage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
child: PhotoViewGallery.builder(
|
||||||
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
|
builder: (BuildContext context, int index) {
|
||||||
|
return PhotoViewGalleryPageOptions(
|
||||||
|
imageProvider: widget.displayImageSources[index].imageProvider,
|
||||||
|
initialScale: PhotoViewComputedScale.contained * 0.8,
|
||||||
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
|
tag: widget.displayImageSources[index].hashCode),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemCount: widget.displayImageSources.length,
|
||||||
|
loadingBuilder: (context, event) => Center(
|
||||||
|
child: Container(
|
||||||
|
width: 20.0,
|
||||||
|
height: 20.0,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value: event == null
|
||||||
|
? 0
|
||||||
|
: event.cumulativeBytesLoaded / event.expectedTotalBytes,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,11 @@ class AuthServiceImp implements AuthService {
|
|||||||
return authFb.getUser(refreshIdToken: refreshIdToken);
|
return authFb.getUser(refreshIdToken: refreshIdToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<User> getUserStream(String userID) {
|
||||||
|
return authFb.user(userID);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Setting> getSetting() {
|
Stream<Setting> getSetting() {
|
||||||
return authFb.settings();
|
return authFb.settings();
|
||||||
@@ -47,6 +52,11 @@ class AuthServiceImp implements AuthService {
|
|||||||
return authFb.signup(userName);
|
return authFb.signup(userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<User> joinInvite(String userName) {
|
||||||
|
return authFb.joinInvite(userName);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<User> onAuthStatus() {
|
Stream<User> onAuthStatus() {
|
||||||
return authFb.onAuthStatus;
|
return authFb.onAuthStatus;
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ abstract class AuthService {
|
|||||||
Future<void> signout();
|
Future<void> signout();
|
||||||
Future<User> getUser({bool refreshIdToken = false});
|
Future<User> getUser({bool refreshIdToken = false});
|
||||||
Future<User> signup(String userName);
|
Future<User> signup(String userName);
|
||||||
|
Future<User> joinInvite(String userName);
|
||||||
Future<void> updateProfile(String newUserName);
|
Future<void> updateProfile(String newUserName);
|
||||||
Future<bool> hasInvite();
|
Future<bool> hasInvite();
|
||||||
|
Stream<User> getUserStream(String userID);
|
||||||
Stream<Setting> getSetting();
|
Stream<Setting> getSetting();
|
||||||
Stream<User> onAuthStatus();
|
Stream<User> onAuthStatus();
|
||||||
Future<String> getToken();
|
Future<String> getToken();
|
||||||
|
|||||||
Reference in New Issue
Block a user