Skip to content

Instantly share code, notes, and snippets.

@maverick42
Last active May 2, 2023 20:33
Show Gist options
  • Save maverick42/eb1ea3415a1f024fe28b9d677741e483 to your computer and use it in GitHub Desktop.
Save maverick42/eb1ea3415a1f024fe28b9d677741e483 to your computer and use it in GitHub Desktop.
MobDR
import 'package:objectbox/objectbox.dart';
import 'package:intl/intl.dart';
import 'package:mobdr/service/shared_prefs.dart';
// ignore_for_file: public_member_api_docs
@Entity()
class VisitPhoto {
// specify the id
@Id()
int id = 0;
int id_visite;
int id_photo_typologie;
String image_name;
DateTime date_photo;
int id_photo_mp4;
int photo_privee;
int photo_principale;
String tags;
int uploaded;
VisitPhoto(
{this.id = 0,
required this.id_visite,
required this.id_photo_typologie,
required this.image_name,
this.id_photo_mp4 = 0,
this.photo_privee = 0,
this.photo_principale = 0,
this.tags = '',
DateTime? date_photo,
this.uploaded = 0})
: date_photo = date_photo ?? DateTime.now();
static String? _photosDir = SharedPrefs().photosDir;
String getImage() {
if (_photosDir == null) {
throw Exception('Photos directory not initialized');
}
return '$_photosDir/$image_name';
}
String get dateFormat => DateFormat('dd.MM.yyyy hh:mm:ss').format(date_photo);
VisitPhoto copyWith({
int? id,
int? id_visite,
int? id_photo_typologie,
String? image,
String? image_name,
DateTime? date_photo,
int? id_photo_mp4,
int? photo_privee,
int? photo_principale,
String? tags,
int? uploaded,
}) {
return VisitPhoto(
id: id ?? this.id,
id_visite: id_visite ?? this.id_visite,
id_photo_typologie: id_photo_typologie ?? this.id_photo_typologie,
image_name: image_name ?? this.image_name,
date_photo: date_photo ?? this.date_photo,
id_photo_mp4: id_photo_mp4 ?? this.id_photo_mp4,
photo_privee: photo_privee ?? this.photo_privee,
photo_principale: photo_principale ?? this.photo_principale,
tags: tags ?? this.tags,
uploaded: uploaded ?? this.uploaded,
);
}
/*
Photo.fromJson(Map<String, dynamic> json)
: id_visite = json['id_visite'],
id_photo_typologie = json['id_photo_typologie'],
image = json['image'];
*/
}
import 'package:mobdr/db/box_user.dart';
import 'package:mobdr/db/box_log.dart';
import 'package:mobdr/db/box_etab.dart';
import 'package:mobdr/db/box_photo_competitor.dart';
import 'package:mobdr/db/box_visit.dart';
import 'package:mobdr/db/box_visit_tag.dart';
import 'package:mobdr/db/box_photo.dart';
import 'package:mobdr/db/box_photo_typology.dart';
import 'model.dart';
import 'objectbox.g.dart'; // created by `flutter pub run build_runner build`
/// Provides access to the ObjectBox Store throughout the app.
///
/// Create this in the apps main function.
class ObjectBox {
/// The Store of this app.
late final Store store;
/// A Box of notes.
late final Box<Note> noteBox;
/// A Box of user
late final Box<User> userBox;
/// A Box of etablishment
late final Box<Etab> etabBox;
/// A Box of etablishment competitor
late final Box<EtabCompetitor> etabCompetitorBox;
/// A Box of visit
late final Box<Visit> visitBox;
/// A Box for all visit tag by distributor / language
late final Box<VisitTag> visitTagBox;
/// A Box of visit Photo
late final Box<VisitPhoto> visitPhotoBox;
/// A Box of photo typology
late final Box<PhotoTypology> PhotoTypologyBox;
/// A Box of log.
late final Box<Log> logBox;
ObjectBox._create(this.store) {
noteBox = Box<Note>(store);
userBox = Box<User>(store);
etabBox = Box<Etab>(store);
etabCompetitorBox = Box<EtabCompetitor>(store);
visitBox = Box<Visit>(store);
visitTagBox = Box<VisitTag>(store);
PhotoTypologyBox = Box<PhotoTypology>(store);
visitPhotoBox = Box<VisitPhoto>(store);
logBox = Box<Log>(store);
// Add some demo data if the box is empty.
if (noteBox.isEmpty()) {
_putDemoData();
}
userBox.removeAll();
/*
etabBox.removeAll();
etabCompetitorBox.removeAll();
visitBox.removeAll();
visitTagBox.removeAll();
visitPhotoBox.removeAll();
PhotoTypologyBox.removeAll();
*/
// Add some demo data if the box is empty.
if (userBox.isEmpty()) {
_putUserAdminData();
}
}
/// Create an instance of ObjectBox to use throughout the app.
static Future<ObjectBox> create() async {
// Future<Store> openStore() {...} is defined in the generated objectbox.g.dart
final store = await openStore();
return ObjectBox._create(store);
}
void _putDemoData() {
final demoNotes = [
Note('Quickly add a note by writing text and pressing Enter'),
Note('Delete notes by tapping on one'),
Note('Write a demo app for ObjectBox')
];
store.runInTransactionAsync(TxMode.write, _putNotesInTx, demoNotes);
}
void _putUserAdminData() {
//addUSer(0, 'root', 'admim', 'admin', '');
}
Stream<List<Note>> getNotes() {
// Query for all notes, sorted by their date.
// https://docs.objectbox.io/queries
final builder = noteBox.query().order(Note_.date, flags: Order.descending);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
.watch(triggerImmediately: true)
// Map it to a list of notes to be used by a StreamBuilder.
.map((query) => query.find());
}
static void _putNotesInTx(Store store, List<Note> notes) =>
store.box<Note>().putMany(notes);
/// Add a note within a transaction.
///
/// To avoid frame drops, run ObjectBox operations that take longer than a
/// few milliseconds, e.g. putting many objects, in an isolate with its
/// own Store instance.
/// For this example only a single object is put which would also be fine if
/// done here directly.
Future<void> addNote(String text) =>
store.runInTransactionAsync(TxMode.write, _addNoteInTx, text);
/// Note: due to [dart-lang/sdk#36983](https://github.com/dart-lang/sdk/issues/36983)
/// not using a closure as it may capture more objects than expected.
/// These might not be send-able to an isolate. See Store.runAsync for details.
static void _addNoteInTx(Store store, String text) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<Note>().put(Note(text));
}
/// USER ---------------------------------------------------------------------
///
Future<void> addUSer(int _id_utilisateur, String _login, String _nom,
String _prenom, String _photo) =>
store.runInTransactionAsync(
TxMode.write,
_addUserInTx,
User(
id_utilisateur: _id_utilisateur,
login: _login,
nom: _nom,
prenom: _prenom,
photo: _photo));
static void _addUserInTx(Store store, _User) {
store.box<User>().put(_User);
}
String getUserAvatar(int id_utilisateur) {
final query =
userBox.query(User_.id_utilisateur.equals(id_utilisateur)).build();
var p = query.findFirst();
return p!.photo;
}
/// ETAB ---------------------------------------------------------------------
///
// A function that converts a response body into a List<Etab>.
List<Etab> parseEtabs(List responseDataList) {
final parsed = responseDataList.cast<Map<String, dynamic>>();
return parsed.map<Etab>((json) => Etab.fromJson(json)).toList();
}
Future<void> addEtabs(List<dynamic> _listEtabs) =>
store.runInTransactionAsync(
TxMode.write, _addEtabsInTx, parseEtabs(_listEtabs));
static void _addEtabsInTx(Store store, _Etabs) {
store.box<Etab>().putMany(_Etabs);
}
Future<void> addEtab(int _id_etab, String _nom, String _mail, String _tel,
String _photo_principale, String _longitude, String _latitude) =>
store.runInTransactionAsync(
TxMode.write,
_addEtabInTx,
Etab(
id_etab: _id_etab,
nom: _nom,
mail: _mail,
tel: _tel,
url_photo_principale: _photo_principale,
longitude: _longitude,
latitude: _latitude));
static void _addEtabInTx(Store store, _Etab) {
store.box<Etab>().put(_Etab);
}
String getEtabPhotoPrincipale(int _id_etab) {
final query = etabBox.query(Etab_.id_etab.equals(_id_etab)).build();
var p = query.findFirst();
return p!.url_photo_principale;
}
int getEtabCount() {
return etabBox.count();
}
/// ETAB COMPETITOR ---------------------------------------------------------------
///
// A function that converts a response body list into a List<Competitor>.
List<EtabCompetitor> parseEtabCompetitor(List responseDataList) {
final parsed = responseDataList.cast<Map<String, dynamic>>();
return parsed
.map<EtabCompetitor>((json) => EtabCompetitor.fromJson(json))
.toList();
}
Future<void> addEtabCompetitors(List<dynamic> _listPhotoCompetitors) =>
store.runInTransactionAsync(TxMode.write, _addEtabCompetitorsInTx,
parseEtabCompetitor(_listPhotoCompetitors));
static void _addEtabCompetitorsInTx(Store store, _Competitors) {
store.box<EtabCompetitor>().putMany(_Competitors);
}
Future<void> addEtabCompetitor(
int _id_concurrence_lien, int _id_etab, _nom) =>
store.runInTransactionAsync(
TxMode.write,
_addEtabCompetitorInTx,
EtabCompetitor(
id_concurrence_lien: _id_concurrence_lien,
id_etab: _id_etab,
nom: _nom,
));
static void _addEtabCompetitorInTx(Store store, _Competitor) {
store.box<EtabCompetitor>().put(_Competitor);
}
List<EtabCompetitor> getEtabCompetitorList() {
final query = etabCompetitorBox
.query(EtabCompetitor_.id_etab.equals(1417))
.order(EtabCompetitor_.nom)
.build();
final photoCompetitors = query.find();
return photoCompetitors.toList();
}
int getEtabCompetitorsCount() {
return etabCompetitorBox.count();
}
/// VISITE ------------------------------------------------------------------
///
Stream<List<Visit>> getVisitStream() {
// Query for all visites, sorted by their date.
// https://docs.objectbox.io/queries
final builder =
visitBox.query().order(Visit_.date_visite, flags: Order.descending);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
.watch(triggerImmediately: true)
// Map it to a list of notes to be used by a StreamBuilder.
.map((query) => query.find());
}
List<Visit> getAllVisit() {
// Query for all visites, sorted by their date.
final builder = visitBox
.query()
.order(Visit_.date_visite, flags: Order.descending)
.build();
// Execute the query and return the result.
return builder.find();
}
List<Visit> getTodayVisit() {
final now = DateTime.now();
// Get the start and end of the current day as timestamps.
final startOfDay =
DateTime(now.year, now.month, now.day).millisecondsSinceEpoch;
final endOfDay = DateTime(now.year, now.month, now.day, 23, 59, 59)
.millisecondsSinceEpoch;
// Query for all visits that occur on the current day and have not been abandoned.
final builder = visitBox
.query(Visit_.date_visite.between(startOfDay, endOfDay) &
Visit_.abandon.equals(0))
.order(Visit_.date_visite, flags: Order.descending)
.build();
// Execute the query and return the result.
return builder.find();
}
List<Visit> getPreviousVisit() {
// Get the previous date at midnight.
final now = DateTime.now();
final midnight = DateTime(now.year, now.month, now.day);
// Convert the date to an integer.
final millisecondsSinceEpoch = midnight.millisecondsSinceEpoch;
// Query for all visits that match the date and abandonment criteria, sorted by their date.
final builder = visitBox
.query(
Visit_.date_visite.lessThan(millisecondsSinceEpoch) &
Visit_.abandon.equals(0),
)
.order(Visit_.date_visite, flags: Order.descending)
.build();
// Execute the query and return the result.
return builder.find();
}
// A function that converts a response body list into a List<Visite>.
List<Visit> parseVisit(List responseDataList) {
final parsed = responseDataList.cast<Map<String, dynamic>>();
return parsed.map<Visit>((json) => Visit.fromJson(json)).toList();
}
// TODO : Enregistre urlphotoprincipale sur le disque pour l'avoir en cache ?
Future<void> addVisits(List<dynamic> _listVisits) =>
store.runInTransactionAsync(
TxMode.write, _addVisitsInTx, parseVisit(_listVisits));
static void _addVisitsInTx(Store store, _Visits) {
store.box<Visit>().putMany(_Visits);
}
Future<void> addVisit(
int _id_visite,
DateTime _date_visite,
DateTime _date_debut,
DateTime _date_fin,
DateTime _date_validation,
String _type_visite,
String _title,
bool _allDay,
String _start,
int _id_distrib_visite,
int _id_etab,
int _abandon,
String _end,
String _url_photo_principale,
String _langage) =>
store.runInTransactionAsync(
TxMode.write,
_addVisitInTx,
Visit(
id_visite: _id_visite,
date_visite: _date_visite,
date_debut: _date_debut,
date_fin: _date_fin,
date_validation: _date_validation,
type_visite: _type_visite,
title: _title,
allDay: _allDay,
id_distrib_visite: _id_distrib_visite,
id_etab: _id_etab,
abandon: _abandon,
url_photo_principale: _url_photo_principale,
langage: _langage));
static void _addVisitInTx(Store store, _Visite) {
store.box<Visit>().put(_Visite);
}
Future<void> syncVisits(List<dynamic> _listVisits) async {
final toSyncId_visite =
_listVisits.map<int>((v) => v['id_visite']).toList();
final existingVisitsQuery =
visitBox.query(Visit_.id_visite.oneOf(toSyncId_visite));
final existingVisits = existingVisitsQuery.build().find();
final existingVisitIds =
existingVisits.map<int>((v) => v.id_visite).toList();
final newVisits = _listVisits
.where((v) => !existingVisitIds.contains(v['id_visite']))
.toList();
final updatedVisits = _listVisits
.where((v) => existingVisitIds.contains(v['id_visite']))
.toList();
newVisits.forEach((v) {
visitBox.put(Visit.fromJson(v));
});
updatedVisits.forEach((v) {
final existingVisit =
existingVisits.firstWhere((ev) => ev.id_visite == v['id_visite']);
final updatedVisit = Visit.fromJson(v)..id = existingVisit.id;
visitBox.put(updatedVisit);
});
}
int getVisitCount() {
return visitBox.count();
}
/// VISIT TAG ---------------------------------------------------------------
///
// converts a response body list into a List<VisitTag>.
List<VisitTag> parseVisitTags(List responseDataList) {
final parsed = responseDataList.cast<Map<String, dynamic>>();
return parsed.map<VisitTag>((json) => VisitTag.fromJson(json)).toList();
}
Future<void> addVisitTags(List<dynamic> _listVisitTags) =>
store.runInTransactionAsync(
TxMode.write, _addVisitTagsInTx, parseVisitTags(_listVisitTags));
static void _addVisitTagsInTx(Store store, _VisitTags) {
store.box<VisitTag>().putMany(_VisitTags);
}
/// save new tag for a distributor / langage
Future<void> addVisitTag(
int id_visite_tag,
int _id_distrib,
String _libelle,
String _langage,
) =>
store.runInTransactionAsync(
TxMode.write,
_addVisitTagInTx,
VisitTag(
id_visite_tag: id_visite_tag,
id_distrib: _id_distrib,
libelle: _libelle,
langage: _langage,
));
static void _addVisitTagInTx(Store store, _VisitTag) {
store.box<VisitTag>().put(_VisitTag);
}
List<String> getVisitTagsLabels(int distribId, String langage) {
final query = visitTagBox
.query(VisitTag_.id_distrib.equals(distribId) &
VisitTag_.langage.equals(langage))
//.order(VisitTag_.libelle)
.build();
PropertyQuery<String> pq = query.property(VisitTag_.libelle);
pq.distinct = true;
pq.caseSensitive = false;
return pq.find();
}
int getVisitTagCount() {
return visitTagBox.count();
}
/// PHOTO --------------------------------------------------------------------
///
///
Future<List<VisitPhoto>> addPhotos(List<VisitPhoto> photos) async {
final addedPhotos = await visitPhotoBox.putAndGetManyAsync(photos);
return addedPhotos;
}
Future<void> addPhoto(
int id_visite, int id_photo_typologie, String image_name) =>
store.runInTransactionAsync(
TxMode.write,
_addPhotoInTx,
VisitPhoto(
id_visite: id_visite,
id_photo_typologie: id_photo_typologie,
image_name: image_name));
static void _addPhotoInTx(Store store, _Photo) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<VisitPhoto>().put(_Photo);
}
List<VisitPhoto> getAllVisitTypologyPhotos(
int id_visite, int _id_photo_typologie) {
// Query for all photos, sorted by their date.
// https://docs.objectbox.io/queries
final query = visitPhotoBox
.query(VisitPhoto_.id_visite.equals(id_visite) &
VisitPhoto_.id_photo_typologie.equals(_id_photo_typologie))
.order(VisitPhoto_.date_photo, flags: Order.descending)
.build();
return query.find();
}
/// Retrieves a Photo object from the ObjectBox database with the specified ID.
///
/// Parameters:
/// id_photo: The ID of the photo to retrieve.
///
/// Returns:
/// A Photo object, or null if no object is found.
VisitPhoto? getPhotoById(int id_photo) {
final photo = visitPhotoBox.get(id_photo);
return photo;
}
Stream<List<VisitPhoto>> getPhotos() {
// Query for all photos, sorted by their date.
// https://docs.objectbox.io/queries
final builder = visitPhotoBox
.query()
.order(VisitPhoto_.date_photo, flags: Order.descending);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
.watch(triggerImmediately: true)
// Map it to a list of notes to be used by a StreamBuilder.
.map((query) => query.find());
}
void delPhoto(String _name) {
final query =
visitPhotoBox.query(VisitPhoto_.image_name.equals(_name)).build();
final results = query.find();
if (results.isNotEmpty) {
final photoToDelete = results.first;
visitPhotoBox.remove(photoToDelete.id);
}
}
int getVisitPhotoCount(int _id_visite) {
final builder =
visitPhotoBox.query(VisitPhoto_.id_visite.equals(_id_visite)).build();
return builder.count();
}
int getVisitTypologiePhotoCount(int _id_visite, int _id_photo_typologie) {
final builder = visitPhotoBox
.query(VisitPhoto_.id_visite.equals(_id_visite) &
VisitPhoto_.id_photo_typologie.equals(_id_photo_typologie))
.build();
return builder.count();
}
Future<void> putPhotoTypologie(int photoId, int typologieId) async {
final photo = visitPhotoBox.get(photoId);
if (photo != null) {
final updatedPhoto = photo.copyWith(id_photo_typologie: typologieId);
await visitPhotoBox.putAsync(updatedPhoto);
}
}
Future<void> putPhotoTags(int photoId, List<String> tags) async {
final photo = visitPhotoBox.get(photoId);
if (photo != null) {
final updatedPhoto = photo.copyWith(tags: tags.join(","));
await visitPhotoBox.putAsync(updatedPhoto);
}
}
Future<void> putPhotoVisibilities(
int photoId, List<String> visibilities) async {
final photo = visitPhotoBox.get(photoId);
if (photo != null) {
final updatedPhoto = photo.copyWith(
photo_principale: visibilities.contains('principal') ? 1 : 0,
photo_privee: visibilities.contains('private') ? 1 : 0);
await visitPhotoBox.putAsync(updatedPhoto);
}
}
/* remettre les principal à zero
final queryBuilder = box.query(VisitPhoto_.Visit_id.equals(idVisite) & VisitPhoto_.photo_principale.equals(1));
final updatedPhotos = queryBuilder.build().find();
updatedPhotos.forEach((photo) {
photo.photo_principale = 0;
});
box.putMany(updatedPhotos);
*/
/// PHOTO TYPOLOGY -----------------------------------------------------------
///
List<PhotoTypology> parsePhotoTypologies(List responseDataList) {
final parsed = responseDataList.cast<Map<String, dynamic>>();
return parsed
.map<PhotoTypology>((json) => PhotoTypology.fromJson(json))
.toList();
}
Future<void> addPhotoTypologies(List<dynamic> _listPhotoTypologies) =>
store.runInTransactionAsync(TxMode.write, _addPhotoTypologiesInTx,
parsePhotoTypologies(_listPhotoTypologies));
static void _addPhotoTypologiesInTx(Store store, _PhotoTypologies) {
store.box<PhotoTypology>().putMany(_PhotoTypologies);
}
Future<void> addPhotoTypology(
int id_photo_typologie, String libelle, int ordre) =>
store.runInTransactionAsync(
TxMode.write,
_addPhotoTypologyInTx,
PhotoTypology(
id_photo_typologie: id_photo_typologie,
libelle: libelle,
ordre: ordre));
static void _addPhotoTypologyInTx(Store store, _PhotoTypology) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<PhotoTypology>().put(_PhotoTypology);
}
List<String> getPhotoTypologiesLabels() {
final query = PhotoTypologyBox.query().order(PhotoTypology_.ordre).build();
final photoTypologies = query.find();
return photoTypologies
.map((photoTypology) => photoTypology.libelle)
.toList();
}
List<PhotoTypology> getPhotoTypologiesList() {
final query = PhotoTypologyBox.query().order(PhotoTypology_.ordre).build();
final photoTypologies = query.find();
return photoTypologies.toList();
}
Stream<List<PhotoTypology>> getPhotoTypologies() {
// Query for all Typologies, sorted by their order.
// https://docs.objectbox.io/queries
final builder = PhotoTypologyBox.query().order(PhotoTypology_.ordre);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
.watch(triggerImmediately: true)
// Map it to a list of notes to be used by a StreamBuilder.
.map((query) => query.find());
}
/// LOG ----------------------------------------------------------------------
///
Future<void> addLog(String type, String module, String libelle, int duree) =>
store.runInTransactionAsync(
TxMode.write,
_addLogInTx,
Log(
type,
module,
libelle,
duree,
));
static void _addLogInTx(Store store, _Log) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<Log>().put(_Log);
}
Stream<List<Log>> getLogs() {
// Query for all logs, sorted by their date.
// https://docs.objectbox.io/queries
final builder = logBox.query().order(Log_.date, flags: Order.descending);
// Build and watch the query,
// set triggerImmediately to emit the query immediately on listen.
return builder
.watch(triggerImmediately: true)
// Map it to a list of notes to be used by a StreamBuilder.
.map((query) => query.find());
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mobdr/main.dart';
import 'package:mobdr/config/constant.dart';
import 'package:mobdr/config/global_style.dart';
import 'package:mobdr/events.dart';
import 'package:mobdr/model/visite_model.dart';
import 'package:mobdr/ui/visit/visit_photo_typology.dart';
import 'package:mobdr/ui/reusable/cache_image_network.dart';
import 'package:mobdr/ui/sync/upload_photos.dart';
class TabVisitListPage extends StatefulWidget {
@override
_TabVisitListPageState createState() => _TabVisitListPageState();
}
class _TabVisitListPageState extends State<TabVisitListPage>
with AutomaticKeepAliveClientMixin {
// _listKey is used for AnimatedList
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
// keep the state to do not refresh when switch navbar
@override
bool get wantKeepAlive => true;
bool _isLoading = true;
String _errorMessage = '';
late List<VisiteModel> modelData = [];
late StreamSubscription subVisitPhotoCountEvent;
@override
void initState() {
super.initState();
// Listen particular event
subVisitPhotoCountEvent = eventBus.on<VisitPhotoCountEvent>().listen((e) {
setState(() {
for (int i = 0; i < modelData.length; i++) {
if (modelData[i].id_visite == e.id_visite) {
modelData[i].photoCount = e.photoCount;
break;
}
}
});
});
loadData().then((_) {
setState(() {
_isLoading = false;
});
});
}
@override
void dispose() {
subVisitPhotoCountEvent.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
super.build(context);
final double boxImageSize = (MediaQuery.of(context).size.width / 4);
if (_isLoading) {
return Center(child: CircularProgressIndicator());
} else if (modelData.isEmpty) {
return Center(
child: Text('No visits to synchronise.'),
);
}
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: GlobalStyle.appBarIconThemeColor,
),
elevation: GlobalStyle.appBarElevation,
title: Text(
'Visit List',
style: GlobalStyle.appBarTitle,
),
backgroundColor: GlobalStyle.appBarBackgroundColor,
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle),
body: Column(children: [
Flexible(
child: AnimatedList(
key: _listKey,
initialItemCount: modelData.length,
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index, animation) {
return _buildVisitelistCard(
modelData[index], boxImageSize, animation, index);
},
)),
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0), //(x,y)
blurRadius: 2.0,
),
],
),
child: Row(
children: [
Container(
child: GestureDetector(
onTap: () {
// TODO functionality to be implemented
/*`
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatUsPage()));
*/
},
child: ClipOval(
child: Container(
color: SOFT_BLUE,
padding: EdgeInsets.all(9),
child: Icon(Icons.filter_list,
color: Colors.white, size: 16)),
),
),
),
SizedBox(
width: 10,
),
Expanded(
child: GestureDetector(
onTap: () {},
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(12, 8, 12, 8),
margin: EdgeInsets.only(right: 8),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 1, color: SOFT_BLUE),
borderRadius: BorderRadius.all(Radius.circular(
10) // <--- border radius here
)),
child: Text('Synchronize ALL',
style: TextStyle(
color: SOFT_BLUE, fontWeight: FontWeight.bold)),
),
),
),
],
),
)
]));
}
Widget _buildVisitelistCard(
VisiteModel visiteData, boxImageSize, animation, index) {
return SizeTransition(
sizeFactor: animation,
child: GestureDetector(
onTap: () {
Route route = MaterialPageRoute(
builder: (context) => VisitPhotoTypologyPage(
pp_id_distrib: visiteData.id_distrib,
pp_langage: visiteData.langage,
pp_id_visite: visiteData.id_visite,
pp_name: visiteData.name));
Navigator.push(context, route);
},
child: Container(
margin: EdgeInsets.fromLTRB(12, 6, 12, 0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 2,
color: Colors.white,
child: Container(
margin: EdgeInsets.all(8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: buildCacheNetworkImage(
width: boxImageSize,
height: boxImageSize,
url: visiteData.image,
),
),
SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
Text(visiteData.name,
style: GlobalStyle.productPrice)
],
),
),
Container(height: 8),
Text(
visiteData.date,
style: GlobalStyle.productSale,
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
'${visiteData.photoCount} Photo(s)',
style: GlobalStyle.productPrice,
),
),
Container(height: 8),
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
Icon(
Icons.store,
color: SOFT_GREY,
size: 20,
),
Text(
' ' + visiteData.type_visite,
style: GlobalStyle.productName.copyWith(
fontSize: 13,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
],
),
),
],
),
)
],
),
Container(
margin: EdgeInsets.only(top: 12),
child: Row(
children: [
Expanded(
child: (visiteData.photoCount == 0)
? TextButton(
style: ButtonStyle(
minimumSize:
MaterialStateProperty.all(Size(0, 30)),
backgroundColor: MaterialStateProperty
.resolveWith<Color>(
(Set<MaterialState> states) =>
Colors.grey[300]!,
),
overlayColor: MaterialStateProperty.all(
Colors.transparent),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
)),
),
onPressed: () {},
child: Text(
'Synchronize',
style: TextStyle(
color: Colors.grey[600],
fontWeight: FontWeight.bold,
fontSize: 13),
textAlign: TextAlign.center,
))
: OutlinedButton(
onPressed: () {
Route route = MaterialPageRoute(
builder: (context) => UploadPhotosPage(
id_visite: visiteData.id_visite,
photoPaths: [
'sim_1682957196322406.jpeg',
],
),
);
Navigator.push(context, route);
},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(
Size(0, 30)),
overlayColor: MaterialStateProperty.all(
Colors.transparent),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5.0),
)),
side: MaterialStateProperty.all(
BorderSide(
color: SOFT_BLUE, width: 1.0),
)),
child: Text(
'Synchronize',
style: TextStyle(
color: SOFT_BLUE,
fontWeight: FontWeight.bold,
fontSize: 13),
textAlign: TextAlign.center,
)),
),
],
),
)
],
),
),
),
),
),
);
}
/// Initializes data when the page loads.
Future<void> loadData() async {
try {
// data initialisation with today visits
modelData = await VisiteModel.getAllVisit();
} catch (e) {
// set errorMessage for debug
_errorMessage = 'Error loading visits : $e';
}
}
}
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mobdr/service/shared_prefs.dart';
import 'package:mobdr/config/constant.dart';
class UploadPhotosPage extends StatefulWidget {
final int id_visite;
final List<String> photoPaths;
UploadPhotosPage({required this.id_visite, required this.photoPaths});
@override
_UploadPhotosPageState createState() => _UploadPhotosPageState();
}
class _UploadPhotosPageState extends State<UploadPhotosPage> {
bool _isUploading = false;
bool _isFinished = false;
int _totalUploaded = 0;
int _totalPhotos = 0;
@override
void initState() {
super.initState();
_totalPhotos = widget.photoPaths.length;
}
Future<void> _uploadPhotos() async {
setState(() {
_isUploading = true;
_isFinished = false;
});
for (int i = 0; i < widget.photoPaths.length; i++) {
String photoPath = SharedPrefs().photosDir + "/" + widget.photoPaths[i];
//String filename = path.basename(photoPath);
// Upload the photo
int photoId = await uploadPhoto(photoPath);
if (photoId != -1) {
_totalUploaded++;
}
}
setState(() {
_isUploading = false;
_isFinished = true;
});
}
Future<int> uploadPhoto(String photoPath) async {
try {
final url = Uri.parse(SERVLET_API);
final file = File(photoPath);
final bytes = await file.readAsBytes();
final multipartRequest = http.MultipartRequest('POST', url)
..fields['id_visite'] = widget.id_visite.toString()
..files.add(http.MultipartFile.fromBytes(
'photo',
bytes,
filename: path.basename(photoPath),
contentType: MediaType('image', 'jpeg'),
));
final headers = {'Cookie': "pguid=${SharedPrefs().guid};"};
multipartRequest.headers.addAll(headers);
final response = await multipartRequest.send();
final responseString = await response.stream.bytesToString();
final jsonResponse = jsonDecode(responseString);
final idPhotoString = jsonResponse[0]['id_photo'];
final idPhoto = int.parse(idPhotoString);
return idPhoto;
} catch (e) {
print('Error uploading photo: $e');
return -1;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Upload Photos'),
centerTitle: true,
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Upload Photos',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Expanded(
child: Stack(
children: [
Center(
child: Visibility(
visible: _isUploading,
child: Container(
height: 150,
width: 150,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor,
),
strokeWidth: 10,
),
),
),
),
Center(
child: Visibility(
visible: !_isUploading && _totalUploaded == _totalPhotos,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
size: 48,
color: Theme.of(context).primaryColor,
),
SizedBox(height: 16),
Text(
'Upload complete',
textAlign: TextAlign.center,
),
],
),
),
),
Center(
child: Visibility(
visible: !_isUploading && _totalUploaded != _totalPhotos,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.cloud_upload,
size: 48,
color: Colors.grey,
),
SizedBox(height: 16),
Text(
'Tap the button below to upload your photos',
textAlign: TextAlign.center,
),
],
),
),
),
],
),
),
SizedBox(height: 16),
Visibility(
visible: !_isUploading,
child: ElevatedButton(
child: Text('Upload Photos ($_totalUploaded / $_totalPhotos)'),
onPressed: _isFinished ? null : _uploadPhotos,
),
),
],
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment