Compare commits

...

101 Commits

Author SHA1 Message Date
Thomas Fransolet
9e0277be6e missing file 2024-04-11 17:36:27 +02:00
Thomas Fransolet
530157bbb2 Update color picker (fix web build) 2024-04-11 17:36:21 +02:00
Thomas Fransolet
785301d908 Update categorie (id for filter and update) 2024-04-11 17:05:57 +02:00
Thomas Fransolet
c2972b11ea rename multi select for language + Fix question quizz not working + other fixs 2024-04-11 16:16:03 +02:00
Thomas Fransolet
4248da3b0d Update service generation 2024-04-10 11:40:42 +02:00
Thomas Fransolet
8893c75792 Added practical infos to geopoint + update single select to dropdown 2024-04-10 11:28:32 +02:00
Thomas Fransolet
649955044f Service generation + init new version + Add weather as a section + download resource + filter geoPoint + added new config params 2024-04-10 10:43:09 +02:00
Thomas Fransolet
fb0a8674d1 Wait 2 sec before refresh ressources (on create) 2024-03-14 15:04:43 +01:00
Thomas Fransolet
8824609fad Clean code + add pick location 2024-03-14 14:41:55 +01:00
Thomas Fransolet
15a11b9eb7 Fix translation and ressource not update + PDF fileS + Agenda map provider + Map handling mapBox 2024-03-12 22:58:35 +01:00
Thomas Fransolet
5fc16ffd0c Small fixs to deploy ! 2024-03-12 16:54:02 +01:00
Thomas Fransolet
afa9ca4fd5 update max size title + fix map type change + category not showed 2024-03-04 16:24:13 +01:00
Thomas Fransolet
5fd5aa5c53 Update agenda resourceIds + update widget selection 2024-02-28 12:01:25 +01:00
Thomas Fransolet
2596a6cc84 filtered language on translation creation 2024-02-21 18:32:15 +01:00
Thomas Fransolet
fec95bbfe8 Added host and complete url for qr code 2024-02-14 15:17:51 +01:00
Thomas Fransolet
e321eda0ac Small fixs 2024-02-09 11:08:43 +01:00
Thomas Fransolet
d014308db9 Update sous menu (handling of Pdf, Puzzle and Agenda) 2024-01-26 21:02:40 +01:00
Thomas Fransolet
09c8cc7cfd Added JsonUrl (to test) 2024-01-24 17:08:06 +01:00
Thomas Fransolet
0f325605e5 Fix bug translation FR 2024-01-17 17:00:24 +01:00
Thomas Fransolet
57c7d56306 clean code + accept empty for descriptions 2024-01-16 16:05:43 +01:00
Thomas Fransolet
c296937530 updated address 2024-01-11 16:16:03 +01:00
Thomas Fransolet
5c714ff8d8 Update policy pages 2024-01-11 16:10:38 +01:00
Thomas Fransolet
7928bc4f7f Fix contentDTO no resourceType 2023-12-29 21:27:31 +01:00
Thomas Fransolet
1d81997a99 Handle resource viewer + add rows and cols to puzzle 2023-12-29 14:59:21 +01:00
Thomas Fransolet
ce815f9950 redirect to prod api 2023-12-28 11:14:14 +01:00
Thomas Fransolet
a646adc550 update service generation + Update Quiz + Puzzle + Map 2023-12-27 18:11:25 +01:00
Thomas Fransolet
0c526fe53e Add Pdf, puzzle and agenda config + adpat other elements + update service generation 2023-12-26 15:48:36 +01:00
Thomas Fransolet
4b7dac07a8 update service generation + gif and webm + wip select resource 2023-12-23 14:34:28 +01:00
Thomas Fransolet
6b65bcbf66 update service generation 2023-12-22 15:41:51 +01:00
Thomas Fransolet
0ffe261e86 Add audio, video, image, pdf and json files (online and local) 2023-12-17 16:14:39 +01:00
Thomas Fransolet
9d099a201c Wip handle new entities and updates for visitNamur 2023-12-15 17:00:57 +01:00
Thomas Fransolet
89d613505a Update docker file to version 2.0.0 2023-12-15 14:55:50 +01:00
Thomas Fransolet
dca7474551 Init version 2.0.0 + wip visitNamur request + wip upload resource to firebase 2023-12-14 17:52:07 +01:00
Thomas Fransolet
ff18f37b1b Add pinCode to manager + show devices tab (fort logic) 2023-12-05 14:38:47 +01:00
Thomas Fransolet
39bc7ca20e Fix snackbar + remove align from quill + add alignment for slider (title preview) 2023-11-02 17:47:03 +01:00
Thomas Fransolet
3a29ec8715 Add language icons + quill editor for title, descriptions 2023-10-31 21:08:49 +01:00
Thomas Fransolet
a7741d8151 Update flutter + dart + all packages + generation + update quill layout (wip) 2023-10-27 16:35:06 +02:00
Thomas Fransolet
2bef54f267 HTML component test + responsive test 2023-06-21 17:21:59 +02:00
2b9e609599 Add version to login screen 2023-04-26 16:09:48 +02:00
ae6dd648c1 increment version to 1.0.1-2 2023-04-26 15:55:33 +02:00
3585a67ee2 Copy QrCode as image + download 2023-04-26 15:42:42 +02:00
9de38c2cfc Fix bug beaconId null 2023-04-17 22:34:45 +02:00
5fadf3943f Fix fail connection 2023-04-02 15:51:18 +02:00
a3a62d1903 Null safety ! 2023-04-01 16:52:13 +02:00
6429ea0ad8 Add isMobile for number sections 2023-03-31 18:23:11 +02:00
Thomas Fransolet
407fe28ead Add update resource label possibility + update layout 2023-03-31 17:13:34 +02:00
Thomas Fransolet
ac91f114b2 misc 2023-03-31 16:45:16 +02:00
Thomas Fransolet
d3f60bcbe6 Add audio visualisation in resource 2023-03-31 16:45:06 +02:00
15f18f9a5a Show number in section 2023-03-30 17:40:43 +02:00
fb4f5e504d Update 2mb to 1.5mb 2023-03-10 20:16:43 +01:00
11f5e2860f Search fix + fix pdf generation with special character 2023-03-10 19:25:22 +01:00
2056fc23fb Rename MyVisit to Mobile 2023-03-10 17:58:25 +01:00
246cb0739a Fix search not working 2023-03-10 17:54:59 +01:00
fb5973d016 View audio title on audio selection + add possibility to choose null on select resource 2023-03-10 17:42:31 +01:00
144722feea Fix audio not showing (empty list) 2023-03-10 16:08:17 +01:00
3186a0dd8e Update section detail layout (display QRCode and beacon section when isMobile) 2023-02-24 14:54:44 +01:00
163ce71e38 Enable quizz section type for mobile and don't show other when MyVisit is true + update readme 2023-02-23 17:22:48 +01:00
Thomas Fransolet
32fc0593bb Add background reference in policy text 2023-02-21 09:54:40 +01:00
Thomas Fransolet
798d3ca451 Update policy page text 2023-02-21 08:57:27 +01:00
c68488ad8d Add policy page 2023-02-20 17:29:54 +01:00
89502ce5d1 Update beaconId as an integer 2023-02-17 17:46:54 +01:00
397e4fb6f2 Update generation 2023-02-17 16:59:08 +01:00
1cbf67d94b Fix image error loading 2023-01-27 21:06:28 +01:00
Thomas Fransolet
e72434ab95 fix commit 2023-01-27 19:31:33 +01:00
Thomas Fransolet
687c979efd MapType fix + add format for geo position + update subsection directly + add quiz edit to menu + filter section type (remove article when no mobile) 2023-01-27 19:30:59 +01:00
8e8be6726b Fix audio translate bug 2023-01-12 17:45:59 +01:00
4dbbd2a335 Handle new language (Arab + Ukrainian) 2023-01-12 16:34:36 +01:00
9fb7250678 Update layout for login for smaller screen 2023-01-06 16:18:59 +01:00
21b38052cc Fix images on languages update + add Beacon handling 2022-12-27 22:46:42 +01:00
509d7b6adf Update service generation 2022-12-27 22:15:28 +01:00
ed9c4bfc15 Update openapi generation .. + update code + fix issue on authenticate try on build 2022-12-22 17:30:37 +01:00
294a1a3b40 Reset prod host 2022-12-22 14:44:06 +01:00
99aaad3f50 Add local storage + update audioIds for Article + fix small layout bugs 2022-12-22 14:32:43 +01:00
d185c2c1e4 Add version 1_3 to release --' 2022-11-03 17:51:35 +01:00
d6b2c5791c Merge remote-tracking branch 'origin/master' into Fort-Article 2022-11-03 17:48:46 +01:00
412746e39a WIP audioplayer view support but not supported, need project upgrade 2022-10-27 22:27:44 +02:00
eddc4005ae Add audio in web + update code for resource .. 2022-10-27 17:32:44 +02:00
ddda68c911 Show only title translations on article images + audio resourcetype + Nom to Identifiant 2022-10-26 17:14:11 +02:00
0f4fd94735 Fix bugs pdf + article missing information.. 2022-10-23 12:02:21 +02:00
e6e20d1a0e Fix label in french + resolve qrcodes font issue 2022-10-21 17:54:19 +02:00
42c442b145 Update logic 2022-10-20 16:18:57 +02:00
375e874c0b Select article type vy default is mobile 2022-10-19 17:19:35 +02:00
7306373277 Filtre de la liste nouvelle section (que article pour isMobile) 2022-10-19 16:19:06 +02:00
164f904f13 Update layout + remove Devices in menu 2022-10-13 17:30:37 +02:00
24ab7796e2 Add instance generation service + dto + not show title and description for article 2022-10-06 17:06:08 +02:00
b6dd44cf9f Docker file + remove host from login page 2022-09-29 17:56:33 +02:00
4e7fcd8d51 Update generation from back + update layout 2022-09-17 22:42:20 +02:00
617f324341 Fix misunderstanding label 2022-09-17 20:22:24 +02:00
7450e1762f Update error color 2022-09-14 18:00:20 +02:00
d30223eb1f Clean code + add icons (mymuseum) 2022-09-14 17:48:58 +02:00
5b0f9cc954 Add languages multi select dialog 2022-09-13 18:54:07 +02:00
85db23ee76 Update loading 2022-09-10 23:10:05 +02:00
7c594bc82d Remove description for detail screen for article + remove all print.. 2022-09-09 15:33:54 +02:00
e64977f95e Add Image select for article type + change icon for qr code download 2022-08-31 17:58:13 +02:00
025338ae9f Add isTitle to 2000 char 2022-08-18 18:13:23 +02:00
e3beb6c7f8 Change reorder section size + add search 2022-06-30 21:08:58 +02:00
98e066c712 PDF QRCode download + code clean 2022-06-23 17:48:02 +02:00
11766b2fac Fix some layout 2022-06-22 18:54:47 +02:00
7195ebb64f Add Article management ! 2022-06-22 18:54:27 +02:00
aa59561dc1 Update article 2022-06-22 17:00:33 +02:00
2b145f757c Generation update + add checkInputContainer (IsTablet, IsOffline, IsMobile) 2022-06-22 16:59:12 +02:00
453 changed files with 143983 additions and 13270 deletions

1
.gitignore vendored
View File

@ -32,7 +32,6 @@
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols

23
Dockerfile Normal file
View File

@ -0,0 +1,23 @@
#Stage 1 - Install dependencies and build the app in a build environment
FROM debian:latest AS build-env
# Install flutter dependencies
RUN apt-get update
RUN apt-get install -y curl git wget unzip libgconf-2-4 gdb libstdc++6 libglu1-mesa fonts-droid-fallback lib32stdc++6 python3 sed
RUN apt-get clean
# Clone the flutter repo
RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter
# Set flutter path
ENV PATH="${PATH}:/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin"
# Run flutter doctor
RUN flutter doctor -v
RUN flutter channel master
RUN flutter upgrade
# Copy files to container and build
RUN mkdir /app/
COPY . /app/
WORKDIR /app/
RUN flutter build web
#--no-sound-null-safety
# Stage 2 - Create the run-time image
FROM nginx:1.21.1-alpine
COPY --from=build-env /app/build/web /usr/share/nginx/html

View File

@ -15,21 +15,31 @@ For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
# Model generation via command line
# Ancienne version de generation = Model generation via command line
java -cp openapi-generator-cli-5.1.0.jar org.openapitools.codegen.OpenAPIGenerator generate -i swagger.yaml --additional-properties pubName=managerapi -g dart --enable-post-process-file
=> Faudrait mettre à jour le projet avec les nouvelles versions plugins
# OPENAPI Generation cmd
flutter clean
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
Le fichier est dans le projet.
# Publication sur docker
Avant de build en docker, il faut build en web => flutter build web
docker build -t flutter-web .
Pour tester en local :
docker run -d -p 8080:80 name flutter-web flutter-web
Image tag:
docker image tag flutter-web registry.unov.be/mymuseum/manager:latest
docker image tag flutter-web registry.unov.be/mymuseum/manager:version-2.0.0
Docker push:
docker image push registry.unov.be/mymuseum/manager:latest
docker image push registry.unov.be/mymuseum/manager:version-2.0.0

View File

@ -0,0 +1 @@
Ajout du Module quizz

View File

@ -0,0 +1,26 @@
- Create BAK folder !!
- FTP fichiers vers rpi (home/Manager_app)
- puis mv -v /home/ubuntu/Manager_app/* /var/www/html/manager/
- Changer nginx : etc/nginx/sites-enabled
- Changer le port du service vers le 5002
- Ajouter ou décommenter (check addresse ip):
server {
listen 8080;
listen [::]:8080;
server_name 192.168.31.96;
root /var/www/html/manager;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
- Tester nginx : sudo nginx -t
- Reload nginx : sudo systemctl reload nginx
Connect manager via http://192.168.1.19:8089 => Si pas ça, peut aller changer dans le main.dart.js puis faire un CTRL MAJ R

View File

@ -0,0 +1 @@
2850532bac056cc3a4316af3c232a077

View File

@ -0,0 +1 @@
{"assets/animations/MDLF_animation.flr":["assets/animations/MDLF_animation.flr"],"assets/files/session.json":["assets/files/session.json"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]}

View File

@ -0,0 +1 @@
[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}]

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

View File

@ -0,0 +1,219 @@
'use strict';
const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"assets/AssetManifest.json": "7ceda6dd13738309d0b4d4d2702b3010",
"assets/assets/animations/MDLF_animation.flr": "5151e91ce20a70dbc097f01a7ec97497",
"assets/assets/files/session.json": "d41d8cd98f00b204e9800998ecf8427e",
"assets/FontManifest.json": "dc3d03800ccca4601324923c0b1d6d57",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/NOTICES": "838205c3fa2fbd0319ce48d7c8347af6",
"assets/packages/cupertino_icons/assets/CupertinoIcons.ttf": "6d342eb68f170c97609e9da345464e5e",
"favicon.png": "5dcef449791fa27946b3d35ad8803796",
"icons/android-icon-144x144.png": "25849c1de85e1ac87dfde295a6c97dfe",
"icons/android-icon-192x192.png": "2f38b513d97a00ddf494059260c193b5",
"icons/android-icon-36x36.png": "5317b23569b5976037738ae5dec04859",
"icons/android-icon-48x48.png": "1f7a48a32ed8b3d4c1e6fa7734534fc7",
"icons/android-icon-72x72.png": "6db0636ec0c228480197e362f3f32fe5",
"icons/android-icon-96x96.png": "d925090c0a2f6b1a24078a8640fb18e4",
"icons/apple-icon-114x114.png": "ddd67f65d43fb30b64ebf7b3ff702427",
"icons/apple-icon-120x120.png": "b6bee39cb32e80068c5e1e87c600ffbc",
"icons/apple-icon-144x144.png": "25849c1de85e1ac87dfde295a6c97dfe",
"icons/apple-icon-152x152.png": "42b1ae2dedce452961803388468d6eae",
"icons/apple-icon-180x180.png": "17655db1c0eaff334ff6208d302a576a",
"icons/apple-icon-57x57.png": "ffd9c2b4dfe93b5874edbef7a3829495",
"icons/apple-icon-60x60.png": "5984c1c3aca2b357f604855446cf30f5",
"icons/apple-icon-72x72.png": "6db0636ec0c228480197e362f3f32fe5",
"icons/apple-icon-76x76.png": "45bb7aabc4d7211f0ee13c77e6300ec4",
"icons/apple-icon-precomposed.png": "9cd7f52d632b4bb32f3b701eed4ec663",
"icons/apple-icon.png": "9cd7f52d632b4bb32f3b701eed4ec663",
"icons/browserconfig.xml": "653d077300a12f09a69caeea7a8947f8",
"icons/favicon-16x16.png": "4fca1fd9f6c90305df787a99fe7abf62",
"icons/favicon-32x32.png": "e78cf52bb5861c5829d6f2b63e0f703e",
"icons/favicon-96x96.png": "91b367951bf21712259a3aa598456d29",
"icons/favicon.ico": "941f61fe5dd76ff538f9fd9456705d7a",
"icons/Icon-192.png": "ac9a721a12bbc803b44f645561ecb1e1",
"icons/Icon-512.png": "96e752610906ba2a93c65f8abe1645f1",
"icons/manifest.json": "b58fcfa7628c9205cb11a1b2c3e8f99a",
"icons/ms-icon-144x144.png": "7f185a22f8c85fb7ccd97cd7711214e5",
"icons/ms-icon-150x150.png": "5e33aababf68472e0bcc89c752e33fec",
"icons/ms-icon-310x310.png": "9eace62e32aec3853604ae2919eca0ea",
"icons/ms-icon-70x70.png": "6720855c40d6a5ba6cd4a384605fc270",
"index.html": "b934f3f00758439d39e79bff55598ca7",
"/": "b934f3f00758439d39e79bff55598ca7",
"main.dart.js": "d92d406acbbcdeb9b4675562b75a0cd3",
"manifest.json": "2ea69ab3cf494be775c9c65d32512a45",
"version.json": "c659aa18797537bf2ce076aa3126b86e"
};
// The application shell files that are downloaded before a service worker can
// start.
const CORE = [
"/",
"main.dart.js",
"index.html",
"assets/NOTICES",
"assets/AssetManifest.json",
"assets/FontManifest.json"];
// During install, the TEMP cache is populated with the application shell files.
self.addEventListener("install", (event) => {
self.skipWaiting();
return event.waitUntil(
caches.open(TEMP).then((cache) => {
return cache.addAll(
CORE.map((value) => new Request(value, {'cache': 'reload'})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self.addEventListener("activate", function(event) {
return event.waitUntil(async function() {
try {
var contentCache = await caches.open(CACHE_NAME);
var tempCache = await caches.open(TEMP);
var manifestCache = await caches.open(MANIFEST);
var manifest = await manifestCache.match('manifest');
// When there is no prior manifest, clear the entire cache.
if (!manifest) {
await caches.delete(CACHE_NAME);
contentCache = await caches.open(CACHE_NAME);
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
return;
}
var oldManifest = await manifest.json();
var origin = self.location.origin;
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if (!RESOURCES[key] || RESOURCES[key] != oldManifest[key]) {
await contentCache.delete(request);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
return;
} catch (err) {
// On an unhandled exception the state of the cache cannot be guaranteed.
console.error('Failed to upgrade service worker: ' + err);
await caches.delete(CACHE_NAME);
await caches.delete(TEMP);
await caches.delete(MANIFEST);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self.addEventListener("fetch", (event) => {
if (event.request.method !== 'GET') {
return;
}
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list then return to signal that the
// browser should take over.
if (!RESOURCES[key]) {
return;
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache.
return response || fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
})
})
);
});
self.addEventListener('message', (event) => {
// SkipWaiting can be used to immediately activate a waiting service worker.
// This will also require a page refresh triggered by the main worker.
if (event.data === 'skipWaiting') {
self.skipWaiting();
return;
}
if (event.data === 'downloadOffline') {
downloadOffline();
return;
}
});
// Download offline will check the RESOURCES for all files not in the cache
// and populate them.
async function downloadOffline() {
var resources = [];
var contentCache = await caches.open(CACHE_NAME);
var currentContent = {};
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
currentContent[key] = true;
}
for (var resourceKey of Object.keys(RESOURCES)) {
if (!currentContent[resourceKey]) {
resources.push(resourceKey);
}
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
}
throw error;
});
});
})
);
}

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
-->
<base href="/">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter application.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="manager_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="icons/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<title>manager_app</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = '3552968134';
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,59 @@
{
"name": "Manager app",
"short_name": "Manager_app",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "Manager application for MDLF",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/android-icon-36x36.png",
"sizes": "36x36",
"type": "image/png",
"density": "0.75"
},
{
"src": "icons/android-icon-48x48.png",
"sizes": "48x48",
"type": "image/png",
"density": "1.0"
},
{
"src": "icons/android-icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"density": "1.5"
},
{
"src": "icons/android-icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"density": "2.0"
},
{
"src": "icons/android-icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"density": "3.0"
},
{
"src": "icons/android-icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"density": "4.0"
},
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

View File

@ -0,0 +1 @@
{"app_name":"manager_app","version":"1.0.0","build_number":"1","package_name":"manager_app"}

28
analysis_options.yaml Normal file
View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,111 @@
<h1>Privacy Policy</h1>
<p>Effective date: 2023-02-20</p>
<p>Updated on: 2023-02-20</p>
<p>
This Privacy Policy explains the policies of UNOV on the collection
and use of the information we collect when you access https://manager.myinfomate.be/ (the “Service”). This Privacy Policy describes your
privacy rights and how you are protected under privacy laws.</p>
<p>
By using our Service, you are consenting to the collection and use of your information in accordance
with this Privacy Policy. Please do not access or use our Service if you do not consent to the collection
and use of your information as outlined in this Privacy Policy. This Privacy Policy has been created with
the help of <a target="_blank" href="https://cookie-script.com/privacy-policy-generator">CookieScript Privacy Policy Generator</a>.</p>
<p>UNOV is authorized to modify this Privacy Policy at any time.
This may occur without prior notice.</p>
<p>
UNOV will post the revised Privacy Policy on the https://manager.myinfomate.be/ website</p>
<h3>Collection and Use of Your Personal Information</h3>
<h4>Information We Collect</h4>
<p>
When using our Service, you will be prompted to provide us with personal information used to
contact or identify you. https://manager.myinfomate.be/ collects the following information: </p>
<ul>
<li>Usage Data</li>
</ul>
<p>Usage Data includes the following:</p>
<ul>
<li>Internet Protocol (IP) address of computers accessing the site</li>
<li>Web page requests</li>
<li>Referring web pages</li>
<li>Browser used to access site</li>
<li>Time and date of access</li>
</ul>
<h4>How We Collect Information</h4>
<p>
https://manager.myinfomate.be/ collects and receives information from you in the following manner: </p>
<ul>
<li>When you fill a registration form or otherwise submit your personal information.</li>
</ul>
<p>
Your information will be stored for up to 1 days after it is
no longer required to provide you the services. Your information may be retained for longer periods for
reporting or record- keeping in accordance with applicable laws. Information which does not identify you
personally may be stored indefinitely. </p>
<h4>How We Use Your Information</h4>
<p>
https://manager.myinfomate.be/ may use your information for the following purposes: </p>
<ul>
<li><b>Providing and maintaining our Service,</b> as well as monitoring the usage of our Service.</li>
<li><b>For other purposes.</b> UNOV will use your information for data analysis to identify usage
trends or determine the effective of our marketing campaigns when reasonable. We will use
your information to evaluate and improve our Service, products, services, and marketing efforts.</li>
<li>Show content based on location</li>
</ul>
<h4>How We Share Your Information</h4>
<p>
UNOV will share your information, when applicable, in the following situations: </p>
<ul>
<li><b>With your consent.</b> UNOV will share your information for any purpose with your explicit
consent.</li>
</ul>
<h4>Third-party Sharing</h4>
<p>Your information may be disclosed for additional reasons, including:</p>
<ul>
<li>Complying with applicable laws, regulations, or court orders.</li>
<li>Responding to claims that your use of our Service violates third-party rights.</li>
<li>Enforcing agreements you make with us, including this Privacy Policy.</li>
</ul>
<h4>Cookies</h4>
<p>
Cookies are small text files that are placed on your computer by websites that you visit. Websites use
cookies to help users navigate efficiently and perform certain functions. Cookies that are required for
the website to operate properly are allowed to be set without your permission. All other cookies need
to be approved before they can be set in the browser. </p>
<ul>
<li><b>Strictly necessary cookies.</b> Strictly necessary cookies allow core website functionality such as
user login and account management. The website cannot be used properly without strictly necessary cookies.</li>
</ul>
<h4>Security</h4>
<p>
Your informations security is important to us. https://manager.myinfomate.be/
utilizes a range of security measures to prevent the misuse, loss, or alteration of the information you have given us.
However, because we cannot guarantee the security of the information you provide us, you must access our service at your
own risk. </p>
<p>
UNOV is not responsible for the performance of websites
operated by third parties or your interactions with them. When you leave this website, we recommend you review the
privacy practices of other websites you interact with and determine the adequacy of those practices. </p>
<h4>Contact Us</h4>
<p>For any questions, please contact us through the following methods:</p>
<p>Name: UNOV</p>
<p>Address: Rue Mazy 72</p>
<p>Email: contact@unov.be</p>
<p>Website: https://manager.myinfomate.be/</p>
<p>
For any queries or concerns you have related to the processing of your information,
you may contact our Privacy Officer at Fransolet Thomas, contact@unov.be. </p>

View File

@ -0,0 +1,98 @@
PRIVACY POLICY
Effective date: 2023-02-20
Updated on: 2024-01-11
This Privacy Policy explains the policies of UNOV on the collection and use of the information we collect when you use the mobile app "MyInfoMate". This Privacy Policy describes your privacy rights and how you are protected under privacy laws.
By using our Service, you are consenting to the collection and use of your information in accordance with this Privacy Policy. Please do not access or use our
Service if you do not consent to the collection and use of your information as outlined in this Privacy Policy. This Privacy Policy has been created with the
help of CookieScript Privacy Policy Generator.
UNOV is authorized to modify this Privacy Policy at any time. This may occur without prior notice.
UNOV will post the revised Privacy Policy on the https://manager.myinfomate.be/ website
COLLECTION AND USE OF YOUR PERSONAL INFORMATION
INFORMATION WE COLLECT
When using our Service, you will be prompted to provide us with personal information used to contact or identify you. The mobile app "MyInfoMate" collects the following information:
* Usage Data
Usage Data includes the following:
* Internet Protocol (IP) address of computers accessing the site
* Web page requests
* Referring web pages
* Browser used to access site
* Time and date of access
HOW WE COLLECT INFORMATION
The mobile app "MyInfoMate" collects and receives information from you in the following manner:
* When you fill a registration form or otherwise submit your personal information.
Your information will be stored for up to 1 days after it is no longer required to provide you the services. Your information may be retained for longer periods
for reporting or record- keeping in accordance with applicable laws. Information which does not identify you personally may be stored indefinitely.
HOW WE USE YOUR INFORMATION
The mobile app "MyInfoMate" may use your information for the following purposes:
* Providing and maintaining our Service, as well as monitoring the usage of our Service.
* For other purposes. UNOV will use your information for data analysis to identify usage trends or determine the effective of our marketing campaigns when
reasonable. We will use your information to evaluate and improve our Service, products, services, and marketing efforts.
* As explained before, only to show content based on location (The collection of location data also occurs in background)
HOW WE SHARE YOUR INFORMATION
UNOV will share your information, when applicable, in the following situations:
* With your consent. UNOV will share your information for any purpose with your explicit consent.
THIRD-PARTY SHARING
Your information may be disclosed for additional reasons, including:
* Complying with applicable laws, regulations, or court orders.
* Responding to claims that your use of our Service violates third-party rights.
* Enforcing agreements you make with us, including this Privacy Policy.
COOKIES
Cookies are small text files that are placed on your computer by websites that you visit. Websites use cookies to help users navigate efficiently and perform
certain functions. Cookies that are required for the website to operate properly are allowed to be set without your permission. All other cookies need to be
approved before they can be set in the browser.
* Strictly necessary cookies. Strictly necessary cookies allow core website functionality such as user login and account management. The website cannot be used
properly without strictly necessary cookies.
SECURITY
Your informations security is important to us. The mobile app "MyInfoMate" utilizes a range of security measures to prevent the misuse, loss, or
alteration of the information you have given us. However, because we cannot guarantee the security of the information you provide us, you must access our
service at your own risk.
UNOV is not responsible for the performance of websites operated by third parties or your interactions with them. When you leave this website, we recommend you
review the privacy practices of other websites you interact with and determine the adequacy of those practices.
CONTACT US
For any questions, please contact us through the following methods:
Name: UNOV
Address: Rue René Copette 3
Email: contact@unov.be
Website: https://manager.myinfomate.be/
Mobile Application : "MyInfoMate"
For any queries or concerns you have related to the processing of your information, you may contact our Privacy Officer at Fransolet Thomas, contact@unov.be.

Binary file not shown.

BIN
assets/images/ar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/images/cn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
assets/images/de.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

BIN
assets/images/en.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
assets/images/es.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/images/fr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

BIN
assets/images/it.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

BIN
assets/images/nl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

BIN
assets/images/pl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

BIN
assets/images/uk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

View File

@ -0,0 +1,170 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/loading_common.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Screens/Resources/select_resource_modal.dart';
import 'package:manager_api_new/api.dart';
import 'package:provider/provider.dart';
class AudioInputContainer extends StatefulWidget {
final Color color;
final String? label;
final String? initialValue;
final ValueChanged<ResourceDTO> onChanged;
final BoxFit imageFit;
final bool isSmall;
final double fontSize;
const AudioInputContainer({
Key? key,
this.color = kSecond,
this.label,
this.initialValue,
required this.onChanged,
this.imageFit = BoxFit.cover,
this.isSmall = false,
this.fontSize = 20
}) : super(key: key);
@override
_AudioInputContainerState createState() => _AudioInputContainerState();
}
class _AudioInputContainerState extends State<AudioInputContainer> {
String? resourceIdToShow;
@override
void initState() {
resourceIdToShow = widget.initialValue;
super.initState();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if(widget.label != null) Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
widget.label!,
style: TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: widget.fontSize,
textAlign: TextAlign.center,
)
),
Container(
//color: widget.isSmall ? Colors.cyanAccent: Colors.red,
child: Padding(
padding: EdgeInsets.only(left: widget.isSmall ? 5 : 10, top: 10, bottom: 10),
child: Container(
width: size.width *0.125,
height: size.width *0.06,
child: InkWell(
onTap: () async {
ResourceDTO? result = await showSelectResourceModal(
"Sélectionner une ressource",
1,
[ResourceType.Audio],
context
);
if (result != null) {
setState(() {
resourceIdToShow = result.id != null ? result.id : null;
});
widget.onChanged(result);
}
},
child: getElement(context),
),
),
),
),
],
),
);
}
getElement(BuildContext context) {
if (resourceIdToShow != null) {
Size size = MediaQuery.of(context).size;
final appContext = Provider.of<AppContext>(context);
return FutureBuilder(
future: getResource(resourceIdToShow!, appContext),
builder: (context, AsyncSnapshot<ResourceDTO?> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return Container(
decoration: boxDecoration(snapshot.data!, appContext),
child: Center(child: AutoSizeText(snapshot.data!.label!, textAlign: TextAlign.center)),
);
} else {
return Text("No data");
}
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: size.height * 0.1,
child: LoadingCommon()
)
);
}
}
);
} else {
return Container(
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
"Choisir un fichier audio",
style: TextStyle(color: kWhite),
maxLines: 1,
textAlign: TextAlign.center,
)
),
)
);
}
}
Future<ResourceDTO?> getResource(String resourceIdToShow, dynamic appContext) async {
// Just in resource tab detail not here
ResourceDTO? resource = await (appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceGetDetail(resourceIdToShow);
return resource;
}
boxDecoration(ResourceDTO resourceDTO, appContext) {
return BoxDecoration(
shape: BoxShape.rectangle,
color: kWhite,
borderRadius: BorderRadius.circular(20.0),
/*image: new DecorationImage(
fit: widget.imageFit,
image: resourceDTO.type != null ? new NetworkImage(
resourceDTO.type == ResourceType.image ? appContext.getContext().clientAPI.resourceApi.apiClient.basePath+"/api/Resource/"+ resourceDTO.id : resourceDTO.data,
) : null,
),*/
boxShadow: [
BoxShadow(
spreadRadius: 0.5,
blurRadius: 1,
offset: Offset(0, 1.5), // changes position of shadow
),
],
);
}
}

View File

@ -0,0 +1,246 @@
import 'dart:typed_data';
//import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:provider/provider.dart';
import 'package:just_audio/just_audio.dart';
class AudioPlayerFloatingContainer extends StatefulWidget {
const AudioPlayerFloatingContainer({Key? key, required this.audioBytes, required this.resourceURl, required this.isAuto}) : super(key: key);
final Uint8List? audioBytes;
final String resourceURl;
final bool isAuto;
@override
State<AudioPlayerFloatingContainer> createState() => _AudioPlayerFloatingContainerState();
}
class _AudioPlayerFloatingContainerState extends State<AudioPlayerFloatingContainer> {
AudioPlayer player = AudioPlayer();
late Uint8List audiobytes;
bool isplaying = false;
bool audioplayed = false;
int currentpos = 0;
int maxduration = 100;
Duration? durationAudio;
String currentpostlabel = "00:00";
@override
void initState() {
Future.delayed(Duration.zero, () async {
if(widget.audioBytes != null) {
audiobytes = widget.audioBytes!;
}
player.durationStream.listen((Duration? d) { //get the duration of audio
if(d != null) {
maxduration = d.inSeconds;
durationAudio = d;
}
});
//player.bufferedPositionStream
player.positionStream.listen((event) {
if(durationAudio != null) {
currentpos = event.inMilliseconds; //get the current position of playing audio
//generating the duration label
int shours = Duration(milliseconds:durationAudio!.inMilliseconds - currentpos).inHours;
int sminutes = Duration(milliseconds:durationAudio!.inMilliseconds - currentpos).inMinutes;
int sseconds = Duration(milliseconds:durationAudio!.inMilliseconds - currentpos).inSeconds;
int rminutes = sminutes - (shours * 60);
int rseconds = sseconds - (sminutes * 60 + shours * 60 * 60);
String minutesToShow = rminutes < 10 ? '0$rminutes': rminutes.toString();
String secondsToShow = rseconds < 10 ? '0$rseconds': rseconds.toString();
currentpostlabel = "$minutesToShow:$secondsToShow";
setState(() {
//refresh the UI
if(currentpos > player.duration!.inMilliseconds) {
print("RESET ALL");
player.stop();
player.seek(const Duration(seconds: 0));
isplaying = false;
audioplayed = false;
currentpostlabel = "00:00";
}
});
}
});
/*player.onPositionChanged.listen((Duration p){
currentpos = p.inMilliseconds; //get the current position of playing audio
//generating the duration label
int shours = Duration(milliseconds:currentpos).inHours;
int sminutes = Duration(milliseconds:currentpos).inMinutes;
int sseconds = Duration(milliseconds:currentpos).inSeconds;
int rminutes = sminutes - (shours * 60);
int rseconds = sseconds - (sminutes * 60 + shours * 60 * 60);
String minutesToShow = rminutes < 10 ? '0$rminutes': rminutes.toString();
String secondsToShow = rseconds < 10 ? '0$rseconds': rseconds.toString();
currentpostlabel = "$minutesToShow:$secondsToShow";
setState(() {
//refresh the UI
});
});*/
if(widget.isAuto) {
//player.play(BytesSource(audiobytes));
//await player.setAudioSource(LoadedSource(audiobytes));
await player.setUrl(widget.resourceURl);
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}
});
super.initState();
}
@override
void dispose() {
player.stop();
player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
return FloatingActionButton(
backgroundColor: kPrimaryColor.withOpacity(0.7),
onPressed: () async {
if(!isplaying && !audioplayed){
//player.play(BytesSource(audiobytes));
await player.setAudioSource(LoadedSource(audiobytes));
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else if(audioplayed && !isplaying){
//player.resume();
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else{
player.pause();
setState(() {
isplaying = false;
});
}
},
child: isplaying ? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.pause),
Text(currentpostlabel),
],
) : audioplayed ? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.play_arrow),
Text(currentpostlabel),
],
): const Icon(Icons.play_arrow),
/*Column(
children: [
//Text(currentpostlabel, style: const TextStyle(fontSize: 25)),
Wrap(
spacing: 10,
children: [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kSecondColor, // Background color
),
onPressed: () async {
if(!isplaying && !audioplayed){
//player.play(BytesSource(audiobytes));
await player.setAudioSource(LoadedSource(audiobytes));
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else if(audioplayed && !isplaying){
//player.resume();
player.play();
setState(() {
isplaying = true;
audioplayed = true;
});
}else{
player.pause();
setState(() {
isplaying = false;
});
}
},
icon: Icon(isplaying?Icons.pause:Icons.play_arrow),
//label:Text(isplaying?TranslationHelper.getFromLocale("pause", appContext.getContext()):TranslationHelper.getFromLocale("play", appContext.getContext()))
),
/*ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: kSecondColor, // Background color
),
onPressed: () async {
player.stop();
player.seek(const Duration(seconds: 0));
setState(() {
isplaying = false;
audioplayed = false;
currentpostlabel = "00:00";
});
},
icon: const Icon(Icons.stop),
//label: Text(TranslationHelper.getFromLocale("stop", appContext.getContext()))
),*/
],
)
],
),*/
);
}
}
// Feed your own stream of bytes into the player
class LoadedSource extends StreamAudioSource {
final List<int> bytes;
LoadedSource(this.bytes);
@override
Future<StreamAudioResponse> request([int? start, int? end]) async {
start ??= 0;
end ??= bytes.length;
return StreamAudioResponse(
sourceLength: bytes.length,
contentLength: end - start,
offset: start,
stream: Stream.value(bytes.sublist(start, end)),
contentType: 'audio/mpeg',
);
}
}

View File

@ -0,0 +1,87 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
class CheckInputContainer extends StatefulWidget {
final bool? isChecked;
final IconData? icon;
final String label;
final ValueChanged<bool> onChanged;
final double fontSize;
const CheckInputContainer({
Key? key,
this.isChecked,
this.icon,
required this.label,
required this.onChanged,
this.fontSize = 20
}) : super(key: key);
@override
_CheckInputContainerState createState() => _CheckInputContainerState();
}
class _CheckInputContainerState extends State<CheckInputContainer> {
bool? isChecked;
@override
void initState() {
setState(() {
isChecked = widget.isChecked;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Row(
children: [
if(widget.icon != null)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(
widget.icon,
color: kPrimaryColor,
size: 25.0,
),
),
AutoSizeText(
widget.label,
style: new TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: widget.fontSize,
textAlign: TextAlign.center,
),
],
)
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
width: 50,
height: 50,
child: Checkbox(
shape: CircleBorder(),
value: isChecked,
checkColor: Colors.white,
activeColor: kPrimaryColor,
onChanged: (bool? value) {
setState(() {
isChecked = value;
});
widget.onChanged(value!);
},
),
),
),
],
),
);
}
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:flutter_hsvcolor_picker/flutter_hsvcolor_picker.dart';
import 'package:manager_app/Components/rounded_button.dart';
import '../constants.dart';
@ -18,8 +18,13 @@ showColorPicker (Color currentColor, Function onSelect, BuildContext context) {
borderRadius: BorderRadius.all(Radius.circular(20.0))
),
content: SingleChildScrollView(
child: SizedBox(
width: 500,
height: 500,
child: ColorPicker(
pickerColor: currentColor,
color: currentColor,
onChanged: changeColor,
/*pickerColor: currentColor,
onColorChanged: changeColor,
colorPickerWidth: 300.0,
pickerAreaHeightPercent: 0.7,
@ -31,6 +36,7 @@ showColorPicker (Color currentColor, Function onSelect, BuildContext context) {
.only(
topLeft: const Radius.circular(2.0),
topRight: const Radius.circular(2.0),
),*/
),
),
),

View File

@ -1,16 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/color_picker.dart';
class ColorPickerInputContainer extends StatefulWidget {
final String color;
final String? color;
final String label;
final double fontSize;
final ValueChanged<String> onChanged;
const ColorPickerInputContainer({
Key key,
Key? key,
this.color,
this.label,
this.onChanged,
required this.label,
this.fontSize = 25,
required this.onChanged,
}) : super(key: key);
@override
@ -18,12 +20,12 @@ class ColorPickerInputContainer extends StatefulWidget {
}
class _ColorPickerInputContainerState extends State<ColorPickerInputContainer> {
Color colorVar;
Color? colorVar;
@override
void initState() {
setState(() {
colorVar = widget.color == null ? new Color(0x12345678) : new Color(int.parse(widget.color.split('(0x')[1].split(')')[0], radix: 16));
colorVar = widget.color == null ? new Color(0x12345678) : new Color(int.parse(widget.color!.split('(0x')[1].split(')')[0], radix: 16));
});
super.initState();
}
@ -35,14 +37,20 @@ class _ColorPickerInputContainerState extends State<ColorPickerInputContainer> {
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(widget.label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
child: AutoSizeText(
widget.label,
style: TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: widget.fontSize,
textAlign: TextAlign.center,
)
),
Padding(
padding: const EdgeInsets.all(10.0),
child: InkWell(
onTap: () {
showColorPicker(
colorVar,
colorVar!,
(Color color) {
setState(() {
colorVar = color;

View File

@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
class DropDownInputContainer extends StatefulWidget {
final String label;
final List<String> values;
final String? initialValue;
final ValueChanged<String>? onChange;
const DropDownInputContainer({
Key? key,
required this.label,
required this.values,
this.initialValue,
this.onChange,
}) : super(key: key);
@override
_DropDownInputContainerState createState() => _DropDownInputContainerState();
}
class _DropDownInputContainerState extends State<DropDownInputContainer> {
late String? selectedValue;
@override
void initState() {
super.initState();
selectedValue = widget.initialValue;
}
@override
Widget build(BuildContext context) {
return Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
widget.label,
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton<String>(
value: selectedValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.black),
underline: Container(
height: 2,
color: kPrimaryColor,
),
onChanged: (String? newValue) {
setState(() {
selectedValue = newValue;
widget.onChange!(selectedValue!);
});
},
items: widget.values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(fontSize: 15),
),
);
}).toList(),
),
),
],
);
}
}

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/constants.dart';
class DropDownInputContainerCategories extends StatefulWidget {
final String label;
final List<CategorieDTO> categories;
final CategorieDTO? initialValue;
final ValueChanged<CategorieDTO>? onChange;
const DropDownInputContainerCategories({
Key? key,
required this.label,
required this.categories,
required this.initialValue,
this.onChange,
}) : super(key: key);
@override
_DropDownInputContainerCategoriesState createState() => _DropDownInputContainerCategoriesState();
}
class _DropDownInputContainerCategoriesState extends State<DropDownInputContainerCategories> {
List<CategorieDTO> categoriesToShow = [];
CategorieDTO? selectedCategorieDTO;
@override
void initState() {
if(widget.initialValue != null) {
selectedCategorieDTO = widget.categories.firstWhere((element) => element.id == widget.initialValue!.id);
}
List<TranslationDTO> label = [];
label.add(TranslationDTO(language: "FR", value: "Aucune catégorie"));
categoriesToShow.add(CategorieDTO(order: -1, label: label));
categoriesToShow.addAll(widget.categories);
super.initState();
}
@override
Widget build(BuildContext context) {
/*final appContext = Provider.of<AppContext>(context);
Size size = MediaQuery.of(context).size;*/
return Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(widget.label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
),
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton<CategorieDTO>(
value: selectedCategorieDTO,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: kWhite),
underline: Container(
height: 2,
color: kPrimaryColor,
),
onChanged: (CategorieDTO? newValue) {
setState(() {
selectedCategorieDTO = newValue!;
widget.onChange!(selectedCategorieDTO!);
});
},
items: categoriesToShow.map<DropdownMenuItem<CategorieDTO>>((CategorieDTO value) {
return DropdownMenuItem<CategorieDTO>(
value: value,
child: HtmlWidget(
value.label == null ? "" : value.label![0].value!,
textStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w400)
),
);
}).toList(),
),
),
],
);
}
}

View File

@ -1,20 +1,24 @@
import 'package:flutter/material.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
IconData getResourceIcon(elementType) {
switch(elementType) {
case ResourceType.image:
case ResourceType.Image:
return Icons.image;
break;
case ResourceType.imageUrl:
case ResourceType.ImageUrl:
return Icons.image_search; // art_track
break;
case ResourceType.video:
case ResourceType.Audio:
return Icons.audiotrack; // art_track
case ResourceType.Video:
return Icons.slow_motion_video;
break;
case ResourceType.videoUrl:
case ResourceType.VideoUrl:
return Icons.ondemand_video_sharp;
break;
case ResourceType.Pdf:
return Icons.picture_as_pdf_outlined;
case ResourceType.Json:
return Icons.file_present_outlined;
case ResourceType.JsonUrl:
return Icons.file_present_rounded;
}
return Icons.announcement;
}

View File

@ -1,26 +1,30 @@
import 'package:flutter/material.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
IconData getSectionIcon(elementType) {
switch(elementType) {
case SectionType.map:
case SectionType.Map:
return Icons.location_on;
break;
case SectionType.slider:
case SectionType.Slider:
return Icons.collections; // art_track
break;
case SectionType.video:
case SectionType.Video:
return Icons.ondemand_video_rounded;
break;
case SectionType.web:
case SectionType.Web:
return Icons.web;
break;
case SectionType.menu:
case SectionType.Menu:
return Icons.apps_sharp;
break;
case SectionType.quizz:
case SectionType.Quizz:
return Icons.question_answer;
break;
case SectionType.Article:
return Icons.article_outlined;
case SectionType.Pdf:
return Icons.picture_as_pdf_outlined;
case SectionType.Puzzle:
return Icons.extension;
case SectionType.Agenda:
return Icons.calendar_month_outlined;
case SectionType.Weather:
return Icons.sunny;
}
return Icons.menu;
return Icons.question_mark;
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
class FlagDecoration extends StatelessWidget {
final String language;
FlagDecoration({Key? key, required this.language}) : super(key: key);
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
width: 35,
height: 35,
decoration: BoxDecoration(
color: kBackgroundColor,
shape: BoxShape.circle,
//border: Border.all(width: 1.5, color: kSecondGrey),
image: DecorationImage(
fit: BoxFit.contain,
image: AssetImage("assets/images/"+language.toLowerCase()+".png")/*Svg(
"assets/images/"+language.toLowerCase()+".svg",
)*/, //AssetImage("assets/images/"+language+".png"),
),
boxShadow: const [
BoxShadow(
color: kBackgroundColor,
spreadRadius: 0.5,
blurRadius: 5,
offset: Offset(0, 1.5), // changes position of shadow
),
],
),
);
}
}

View File

@ -1,158 +0,0 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/loading.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Screens/Resources/select_resource_modal.dart';
import 'package:managerapi/api.dart';
import 'package:provider/provider.dart';
class ImageInputContainer extends StatefulWidget {
final Color color;
final String label;
final String initialValue;
final ValueChanged<ResourceDTO> onChanged;
final BoxFit imageFit;
final bool isSmall;
const ImageInputContainer({
Key key,
this.color = kSecond,
this.label,
this.initialValue,
this.onChanged,
this.imageFit = BoxFit.cover,
this.isSmall = false,
}) : super(key: key);
@override
_ImageInputContainerState createState() => _ImageInputContainerState();
}
class _ImageInputContainerState extends State<ImageInputContainer> {
String resourceIdToShow;
@override
void initState() {
resourceIdToShow = widget.initialValue;
super.initState();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(widget.label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
),
Padding(
padding: EdgeInsets.only(left: widget.isSmall ? 15 : 70, top: 10, bottom: 10),
child: Container(
width: size.width *0.08,
child: InkWell(
onTap: () async {
var result = await showSelectResourceModal(
"Sélectionner une ressource",
1,
true,
context
);
if (result != null) {
setState(() {
resourceIdToShow = result.id;
});
widget.onChanged(result);
}
},
child: getElement(widget.initialValue, context, widget.isSmall),
),
),
),
],
),
);
}
getElement(String initialValue, BuildContext context, bool isSmall) {
if (resourceIdToShow != null) {
Size size = MediaQuery.of(context).size;
final appContext = Provider.of<AppContext>(context);
return FutureBuilder(
future: getResource(resourceIdToShow, appContext),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return Transform.scale(
scale: isSmall ? size.aspectRatio * 0.5: size.aspectRatio * 0.9,
child: AspectRatio(
aspectRatio: 4/4,
child: Container(
decoration: boxDecoration(snapshot.data, appContext),
),
),
);
} else {
return Text("No data");
}
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: size.height * 0.1,
child: Loading()
)
);
}
}
);
} else {
return Container(
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(50),
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
"Choisir une image",
style: TextStyle(color: kWhite),
maxLines: 1,
)
),
)
);
}
}
Future<ResourceDTO> getResource(String resourceIdToShow, dynamic appContext) async {
ResourceDTO resource = await appContext.getContext().clientAPI.resourceApi.resourceGetDetail(resourceIdToShow);
return resource;
}
boxDecoration(ResourceDTO resourceDTO, appContext) {
return BoxDecoration(
shape: BoxShape.rectangle,
color: kWhite,
borderRadius: BorderRadius.circular(30.0),
image: new DecorationImage(
fit: widget.imageFit,
image: resourceDTO.type != null ? new NetworkImage(
resourceDTO.type == ResourceType.image ? appContext.getContext().clientAPI.resourceApi.apiClient.basePath+"/api/Resource/"+ resourceDTO.id : resourceDTO.data,
) : null,
),
boxShadow: [
BoxShadow(
spreadRadius: 0.5,
blurRadius: 1,
offset: Offset(0, 1.5), // changes position of shadow
),
],
);
}
}

View File

@ -2,7 +2,7 @@ import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/material.dart';
class Loading extends StatelessWidget {
Loading({Key key}) : super(key: key);
Loading({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
class LoadingCommon extends StatefulWidget {
double? iconSize;
LoadingCommon({Key? key, this.iconSize}) : super(key: key);
@override
State<LoadingCommon> createState() => _LoadingCommonState();
}
class _LoadingCommonState extends State<LoadingCommon> with TickerProviderStateMixin {
AnimationController? _controller;
@override
void initState() {
_controller = AnimationController(
duration: const Duration(milliseconds: 5000),
vsync: this,
)..repeat();
super.initState();
}
@override
void dispose() {
_controller!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
_controller!.forward(from: 0.0);
_controller!.addListener(() {
if (_controller!.isCompleted) {
_controller!.reverse();
}
if(_controller!.isDismissed){
_controller!.forward();
}
});
return Center(
child: RotationTransition(
turns: Tween(begin: 0.0, end: 3.0).animate(_controller!),
child: Icon(Icons.museum_outlined, color: kPrimaryColor, size: widget.iconSize == null ? size.height*0.1 : widget.iconSize!),
),
);
}
}

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
showNotification (Color backgroundColor, Color textColor, String text, BuildContext context, int duration) {
showNotification (Color backgroundColor, Color textColor, String text, BuildContext context, int? duration) {
final snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
duration: duration == null ? Duration(milliseconds: 1500) : Duration(milliseconds: duration),
width: 280.0, // Width of the SnackBar.
width: 320.0, // Width of the SnackBar.
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),

View File

@ -1,15 +1,19 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/text_form_input_container.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
import 'package:collection/collection.dart';
import 'package:provider/provider.dart';
showMultiStringInput (String label, String modalLabel, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, BuildContext context) { /*Function onSelect,*/
import 'audio_input_container.dart';
import 'flag_decoration.dart';
showMultiStringInput (String label, String modalLabel, bool isTitle, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context) { /*Function onSelect,*/
showDialog(
builder: (BuildContext context) => AlertDialog(
shape: RoundedRectangleBorder(
@ -23,7 +27,7 @@ showMultiStringInput (String label, String modalLabel, List<TranslationDTO> valu
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: getTranslations(context, Provider.of<AppContext>(context), label, newValues),
children: getTranslations(context, Provider.of<AppContext>(context), label, isTitle, resourceTypes, newValues),
),
),
),
@ -45,7 +49,9 @@ showMultiStringInput (String label, String modalLabel, List<TranslationDTO> valu
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 180,
height: 70,
child: RoundedButton(
@ -59,7 +65,10 @@ showMultiStringInput (String label, String modalLabel, List<TranslationDTO> valu
fontSize: 20,
),
),
Container(
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 180,
height: 70,
child: RoundedButton(
@ -77,6 +86,7 @@ showMultiStringInput (String label, String modalLabel, List<TranslationDTO> valu
fontSize: 20,
),
),
),
],
),
],
@ -84,17 +94,18 @@ showMultiStringInput (String label, String modalLabel, List<TranslationDTO> valu
);
}
getTranslations(BuildContext context, AppContext appContext, String label, List<TranslationDTO> newValues) {
getTranslations(BuildContext context, AppContext appContext, String label, bool isTitle, List<ResourceType>? resourceTypes, List<TranslationDTO> newValues) {
List<Widget> translations = <Widget>[];
ManagerAppContext managerAppContext = appContext.getContext();
for(var language in managerAppContext.selectedConfiguration.languages) {
for(var language in managerAppContext.selectedConfiguration!.languages!) {
translations.add(
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
FlagDecoration(language: language),
/*Container(
width: MediaQuery.of(context).size.width *0.05,
height: MediaQuery.of(context).size.height *0.10,
decoration: BoxDecoration(
@ -103,7 +114,7 @@ getTranslations(BuildContext context, AppContext appContext, String label, List<
),
),
child: Center(child: AutoSizeText(language.toUpperCase()))
),
),*/
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Container(
@ -111,14 +122,37 @@ getTranslations(BuildContext context, AppContext appContext, String label, List<
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
resourceTypes == null ?
TextFormInputContainer(
label: label,
color: kWhite,
isTitle: true,
initialValue: newValues.where((element) => element.language == language).first.value,
isTitle: isTitle,
initialValue: newValues.where((element) => element.language == language).first.value!,
onChanged: (value) {
newValues.where((element) => element.language == language).first.value = value;
},
) :
Container(
width: MediaQuery.of(context).size.width *0.15,
height: 100,
//color: Colors.blueAccent,
child: Center(
child: ResourceInputContainer(
label: "",
initialValue: newValues.where((element) => element.language == language).first.value == null ? null : newValues.where((element) => element.language == language).first.value!,
inResourceTypes: resourceTypes,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language == language).first.value = resource.id;
},
)/*AudioInputContainer(
//label: "Audio :",
initialValue: newValues.where((element) => element.language == language).first.value,
color: kPrimaryColor,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language == language).first.value = resource.id;
},
),*/
),
),
],
),
@ -131,6 +165,7 @@ getTranslations(BuildContext context, AppContext appContext, String label, List<
}
return translations;
}
/*
showValues(List<TranslationDTO> newValues) {
List<Widget> valuesToShow = new List<Widget>();
@ -147,5 +182,3 @@ showValues(List<TranslationDTO> newValues) {
});
return valuesToShow;
}*/

View File

@ -1,46 +1,59 @@
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
import 'package:html/parser.dart' show parse;
import 'message_notification.dart';
class MultiSelectContainer extends StatelessWidget {
final Color color;
final String label;
final String? label;
final List<String> values;
final List<String> initialValue;
final bool isMultiple;
final bool isAtLeastOne;
final bool isHTMLLabel;
final double? width;
final int maxLines;
final ValueChanged<List<dynamic>> onChanged;
const MultiSelectContainer({
Key key,
Key? key,
this.color = kSecond,
this.label,
this.values,
this.initialValue,
this.isMultiple,
required this.label,
required this.values,
required this.initialValue,
required this.isMultiple,
this.isAtLeastOne = false,
this.onChanged,
this.isHTMLLabel = false,
this.width,
this.maxLines = 2,
required this.onChanged,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if(label != null)
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
child: Text(label!, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
width: size.width *0.2,
width: width ?? size.width *0.25,
//color: Colors.yellow,
child: MultiSelectChip(
values,
initialValue,
isMultiple,
isAtLeastOne,
isHTMLLabel,
maxLines,
onSelectionChanged: (selectedList) {
onChanged(selectedList);
},
@ -59,24 +72,30 @@ class MultiSelectChip extends StatefulWidget {
final Function(List<String>) onSelectionChanged; // +added
final bool isMultiple;
final bool isAtLeastOne;
final bool isHTMLLabel;
final int maxLines;
MultiSelectChip(
this.values,
this.selectedValues,
this.isMultiple,
this.isAtLeastOne,
{this.onSelectionChanged} // +added
this.isHTMLLabel,
this.maxLines,
{required this.onSelectionChanged} // +added
);
@override
_MultiSelectChipState createState() => _MultiSelectChipState();
}
class _MultiSelectChipState extends State<MultiSelectChip> {
int maxLines = 1; // Définir le nombre maximum de lignes
_buildChoiceList() {
List<Widget> choices = List();
List<Widget> choices = [];
widget.values.forEach((item) {
choices.add(Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
label: Text(item, style: TextStyle(color: kBlack)),
label: Text(widget.isHTMLLabel ? parse(item).documentElement!.text : item, style: TextStyle(color: kBlack)),
selected: widget.selectedValues.contains(item),
selectedColor: kPrimaryColor,
onSelected: (selected) {
@ -102,10 +121,17 @@ class _MultiSelectChipState extends State<MultiSelectChip> {
});
return choices;
}
@override
Widget build(BuildContext context) {
return Wrap(
return Container(
height: maxLines * 48.0, // Assuming each ChoiceChip is 48.0 height
child: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: _buildChoiceList(),
),
);
}
}

View File

@ -0,0 +1,81 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
import 'package:multi_select_flutter/chip_display/multi_select_chip_display.dart';
import 'package:multi_select_flutter/dialog/multi_select_dialog_field.dart';
import 'package:multi_select_flutter/util/multi_select_item.dart';
import 'package:multi_select_flutter/util/multi_select_list_type.dart';
class MultiSelectDropdownLanguageContainer extends StatelessWidget {
final Color color;
final String label;
final String labelHint;
final List<String> values;
final List<String> initialValue;
final bool isMultiple;
final bool isAtLeastOne;
final double fontSize;
final ValueChanged<List<dynamic>> onChanged;
const MultiSelectDropdownLanguageContainer({
Key? key,
this.color = kSecond,
required this.label,
this.labelHint = "Veuillez sélectionner une langue",
required this.values,
required this.initialValue,
required this.isMultiple,
this.isAtLeastOne = false,
this.fontSize = 25,
required this.onChanged,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
)
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
width: size.width *0.2,
child: MultiSelectDialogField(
items: values.map((e) => MultiSelectItem(e, e)).toList(),
listType: MultiSelectListType.LIST,
cancelText: Text("Annuler"),
initialValue: initialValue,
buttonText: Text("Sélectionner"),
checkColor: Colors.white,
searchable: true,
chipDisplay: MultiSelectChipDisplay.none(),
selectedColor: kPrimaryColor,
title: Text(labelHint),
dialogHeight: size.height *0.4,
dialogWidth: size.width *0.2,
onSelectionChanged: (selectedList) {
onChanged(selectedList);
},
onConfirm: (List<dynamic> test)
{
print("onConfirm MultiSelectDialogField");
print(test);
},
),
),
),
],
),
);
}
}

View File

@ -0,0 +1,97 @@
import 'dart:convert';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/multi_string_input_html_modal.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart';
import 'package:provider/provider.dart';
class MultiStringInputAndResourceContainer extends StatelessWidget {
final Color color;
final String label;
final String modalLabel;
final List<TranslationAndResourceDTO> initialValue;
final Function onGetResult;
final int maxLines;
final bool isTitle;
final List<ResourceType>? resourceTypes;
final double fontSize;
const MultiStringInputAndResourceContainer({
Key? key,
this.color = kSecond,
required this.label,
required this.modalLabel,
required this.initialValue,
required this.onGetResult,
required this.maxLines,
required this.isTitle,
this.resourceTypes = null,
this.fontSize = 25,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
ManagerAppContext managerAppContext = appContext.getContext();
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
)
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
width: size.width *0.15,
child: InkWell(
onTap: () {
List<TranslationAndResourceDTO> newValues = <TranslationAndResourceDTO>[];
List<TranslationAndResourceDTO> initials = initialValue;
managerAppContext.selectedConfiguration!.languages!.forEach((value) {
if(initials.map((iv) => iv.language).contains(value)) {
newValues.add(TranslationAndResourceDTO.fromJson(jsonDecode(jsonEncode(initials.firstWhere((element) => element.language == value)))!)!);
} else {
// New language
newValues.add(TranslationAndResourceDTO(language: value, value: "", resourceType: null, resourceId: null, resourceUrl: null));
}
});
showMultiStringInputAndResourceHTML(label, modalLabel, isTitle, initials, newValues, onGetResult, maxLines, resourceTypes, context);
},
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(50),
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
resourceTypes == null ? "Changer traductions" : "Changer ressources",
style: TextStyle(color: kWhite),
maxLines: 2,
)
),
)
),
),
),
),
],
),
);
}
}

View File

@ -3,28 +3,44 @@ import 'dart:convert';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/multi_input_modal.dart';
import 'package:manager_app/Components/multi_string_input_html_modal.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
import 'package:provider/provider.dart';
class MultiStringContainer extends StatelessWidget {
class MultiStringInputContainer extends StatelessWidget {
final Color color;
final String label;
final String modalLabel;
final List<TranslationDTO> initialValue;
final Function onGetResult;
final int maxLines;
const MultiStringContainer({
Key key,
final bool isTitle;
final List<ResourceType>? resourceTypes;
final bool isHTML;
final double fontSize;
final bool isMandatory;
const MultiStringInputContainer({
Key? key,
this.color = kSecond,
this.label,
this.modalLabel,
this.initialValue,
this.onGetResult,
this.maxLines,
required this.label,
required this.modalLabel,
required this.initialValue,
required this.onGetResult,
required this.maxLines,
required this.isTitle,
this.resourceTypes = null,
this.isHTML = false,
this.fontSize = 25,
this.isMandatory = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final appContext = Provider.of<AppContext>(context);
ManagerAppContext managerAppContext = appContext.getContext();
Size size = MediaQuery.of(context).size;
return Container(
@ -32,7 +48,13 @@ class MultiStringContainer extends StatelessWidget {
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
child: AutoSizeText(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
)
),
Padding(
padding: const EdgeInsets.all(10.0),
@ -40,14 +62,28 @@ class MultiStringContainer extends StatelessWidget {
width: size.width *0.15,
child: InkWell(
onTap: () {
List<TranslationDTO> newValues = new List<TranslationDTO>();
// Make a copy
initialValue.forEach((value) {
newValues.add(TranslationDTO.fromJson(jsonDecode(jsonEncode(value))));
List<TranslationDTO> newValues = <TranslationDTO>[];
List<TranslationDTO> initials = initialValue;
managerAppContext.selectedConfiguration!.languages!.forEach((value) {
if(initials.map((iv) => iv.language).contains(value)) {
newValues.add(TranslationDTO.fromJson(jsonDecode(jsonEncode(initials.firstWhere((element) => element.language == value)))!)!);
} else {
// New language
newValues.add(TranslationDTO(language: value, value: ""));
}
});
showMultiStringInput(label, modalLabel, initialValue, newValues, onGetResult, maxLines, context);
if(isHTML) {
showMultiStringInputHTML(label, modalLabel, isTitle, initials, newValues, onGetResult, maxLines, resourceTypes, context, isMandatory);
} else {
showMultiStringInput(label, modalLabel, isTitle, initials, newValues, onGetResult, maxLines, resourceTypes, context);
}
},
child: Container(
constraints: BoxConstraints(maxHeight: 60),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(50),
@ -56,7 +92,7 @@ class MultiStringContainer extends StatelessWidget {
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
"Changer les traductions",
resourceTypes == null ? "Changer traductions" : "Changer ressources",
style: TextStyle(color: kWhite),
maxLines: 2,
)

View File

@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:manager_app/Components/message_notification.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/translation_input_and_resource_container.dart';
import 'package:manager_app/Components/translation_input_container.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_api_new/api.dart';
import 'package:collection/collection.dart';
showMultiStringInputHTML (String label, String modalLabel, bool isTitle, List<TranslationDTO> values, List<TranslationDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context, bool isMandatory) {
showDialog(
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))
),
title: Center(child: Text(modalLabel)),
content: SingleChildScrollView(
child: TranslationInputContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes)
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Annuler",
icon: Icons.undo,
color: kSecond,
press: () {
onGetResult(values);
Navigator.of(context).pop();
},
fontSize: 20,
),
),
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Valider",
icon: Icons.check,
color: kPrimaryColor,
textColor: kWhite,
press: () {
Function deepEq = const DeepCollectionEquality().equals;
if (!deepEq(values, newValues)) {
if(isMandatory && newValues.any((label) => label.value == null || label.value!.trim() == "")) {
showNotification(kPrimaryColor, kWhite, "La traduction n'est pas complète", context, null);
} else {
onGetResult(newValues);
Navigator.of(context).pop();
}
} else {
Navigator.of(context).pop();
}
},
fontSize: 20,
),
),
],
),
],
);
}, context: context
);
}
showMultiStringInputAndResourceHTML (String label, String modalLabel, bool isTitle, List<TranslationAndResourceDTO> values, List<TranslationAndResourceDTO> newValues, Function onGetResult, int maxLines, List<ResourceType>? resourceTypes, BuildContext context) {
showDialog(
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))
),
title: Center(child: Text(modalLabel)),
content: SingleChildScrollView(
child: TranslationInputAndResourceContainer(isTitle: isTitle, values: values, newValues: newValues, onGetResult: onGetResult, maxLines: maxLines, resourceTypes: resourceTypes)
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Annuler",
icon: Icons.undo,
color: kSecond,
press: () {
onGetResult(values);
Navigator.of(context).pop();
},
fontSize: 20,
),
),
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Valider",
icon: Icons.check,
color: kPrimaryColor,
textColor: kWhite,
press: () {
Function deepEq = const DeepCollectionEquality().equals;
if (!deepEq(values, newValues)) {
if(newValues.any((label) => label.value == null || label.value!.trim() == "")) {
showNotification(kPrimaryColor, kWhite, "La traduction n'est pas complète", context, null);
} else {
onGetResult(newValues);
Navigator.of(context).pop();
}
} else {
Navigator.of(context).pop();
}
},
fontSize: 20,
),
),
],
),
],
);
}, context: context
);
}

View File

@ -0,0 +1,63 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/rounded_input_field.dart';
import 'package:manager_app/constants.dart';
class NumberInputContainer extends StatelessWidget {
final Color color;
final String label;
final int initialValue;
final ValueChanged<String> onChanged;
final bool isUrl;
final bool isSmall;
final int maxLength;
final double fontSize;
final double fontSizeText;
const NumberInputContainer({
Key? key,
this.color = kSecond,
required this.label,
required this.initialValue,
required this.onChanged,
this.isUrl = false,
this.isSmall = false,
this.maxLength = 50,
this.fontSize = 25,
this.fontSizeText = 20,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
width: isUrl ? size.width *0.6 : isSmall ? size.width *0.1 : size.width *0.2,
child: RoundedInputField(
color: color,
textColor: kBlack,
fontSize: fontSizeText,
initialValue: initialValue.toString(),
onChanged: onChanged,
maxLength: maxLength,
),
),
),
],
),
);
}
}

View File

@ -0,0 +1,147 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/Map/category_list.dart';
import 'package:manager_app/Screens/Configurations/Section/SubSection/PDF/pdf_list.dart';
import 'package:manager_app/constants.dart';
class PDFFileInputContainer extends StatefulWidget {
final Color color;
final String label;
List<PDFFileDTO> initialValue;
final ValueChanged<List<PDFFileDTO>> onChanged;
PDFFileInputContainer({
Key? key,
this.color = kSecond,
required this.label,
required this.initialValue,
required this.onChanged,
}) : super(key: key);
@override
_PDFFileInputContainerState createState() => _PDFFileInputContainerState();
}
class _PDFFileInputContainerState extends State<PDFFileInputContainer> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(widget.label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: size.width *0.15,
child: InkWell(
onTap: () {
List<PDFFileDTO> newValues = <PDFFileDTO>[];
List<PDFFileDTO> initials = widget.initialValue;
showCreateOrUpdatePdfFiles("Fichiers PDF", initials, newValues, (value) {
widget.onChanged(value);
widget.initialValue = value;
}, context);
},
child: Container(
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(50),
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
"Changer",
style: TextStyle(color: kWhite),
maxLines: 2,
)
),
)
),
),
),
),
],
),
);
}
}
showCreateOrUpdatePdfFiles(String modalLabel, List<PDFFileDTO> values, List<PDFFileDTO> newValues, Function onGetResult, BuildContext context) {
showDialog(
builder: (BuildContext context) {
Size size = MediaQuery.of(context).size;
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))
),
title: Center(child: Text(modalLabel)),
content: SingleChildScrollView(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(width: 0.75, color: kSecond)
),
height: size.height * 0.7,
width: size.width * 0.65,
child: PDFList(pdfs: values, onChanged: (result) {
newValues = result;
onGetResult(result);
}),
)
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Annuler",
icon: Icons.undo,
color: kSecond,
press: () {
onGetResult(values);
Navigator.of(context).pop();
},
fontSize: 20,
),
),
Container(
width: 180,
height: 70,
child: RoundedButton(
text: "Valider",
icon: Icons.check,
color: kPrimaryColor,
textColor: kWhite,
press: () {
/*Function deepEq = const DeepCollectionEquality().equals;
if (!deepEq(values, newValues)) {
onGetResult(newValues);
}*/
Navigator.of(context).pop();
},
fontSize: 20,
),
),
],
),
],
);
}, context: context
);
}

View File

@ -0,0 +1,187 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/loading_common.dart';
import 'package:manager_app/Models/managerContext.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Screens/Resources/select_resource_modal.dart';
import 'package:manager_api_new/api.dart';
import 'package:provider/provider.dart';
class ResourceInputContainer extends StatefulWidget {
final Color color;
final String label;
final String? initialValue;
final ValueChanged<ResourceDTO> onChanged;
final BoxFit imageFit;
final bool isSmall;
final double fontSize;
final List<ResourceType> inResourceTypes;
final bool isLanguageTab;
const ResourceInputContainer({
Key? key,
this.color = kSecond,
required this.label,
this.initialValue,
required this.onChanged,
this.imageFit = BoxFit.cover,
this.isSmall = false,
this.fontSize = 25,
this.inResourceTypes = const [ResourceType.Image, ResourceType.ImageUrl],
this.isLanguageTab = false
}) : super(key: key);
@override
_ResourceInputContainerState createState() => _ResourceInputContainerState();
}
class _ResourceInputContainerState extends State<ResourceInputContainer> {
String? resourceIdToShow;
@override
void initState() {
resourceIdToShow = widget.initialValue;
super.initState();
}
@override
Widget build(BuildContext context) {
if(widget.isLanguageTab) {
resourceIdToShow = widget.initialValue;
}
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
widget.label,
style: TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: widget.fontSize,
textAlign: TextAlign.center,
)
),
Container(
//color: widget.isSmall ? Colors.cyanAccent: Colors.red,
child: Padding(
padding: EdgeInsets.only(left: widget.isSmall ? 5 : 10, top: 10, bottom: 10),
child: Container(
width: 90,
height: 90,
child: InkWell(
onTap: () async {
ResourceDTO? result = await showSelectResourceModal(
"Sélectionner une ressource",
1,
widget.inResourceTypes,
context
);
if (result != null) {
setState(() {
resourceIdToShow = result.id != null ? result.id : null;
});
widget.onChanged(result);
}
},
child: getElement(widget.initialValue, context, widget.isSmall),
),
),
),
),
],
),
);
}
getElement(String? initialValue, BuildContext context, bool isSmall) {
if (resourceIdToShow != null) {
Size size = MediaQuery.of(context).size;
final appContext = Provider.of<AppContext>(context);
return FutureBuilder(
future: getResource(resourceIdToShow!, appContext),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
ResourceDTO resourceDTO = snapshot.data!;
return Container(
decoration: boxDecoration(snapshot.data, appContext),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(resourceDTO.type == ResourceType.Image || resourceDTO.type == ResourceType.ImageUrl ? "" : resourceDTO.label!),
),
)
);
} else {
return Center(
child: Container(
decoration: boxDecoration(null, appContext),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Aucune ressource"),
)
)
);
}
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: size.height * 0.1,
child: LoadingCommon()
)
);
}
}
);
} else {
return Container(
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15),
child: Center(
child: AutoSizeText(
"Choisir",
style: TextStyle(color: kWhite),
maxLines: 1,
)
),
)
);
}
}
Future<ResourceDTO?> getResource(String resourceIdToShow, dynamic appContext) async {
ResourceDTO? resource = await (appContext.getContext() as ManagerAppContext).clientAPI!.resourceApi!.resourceGetDetail(resourceIdToShow);
return resource;
}
boxDecoration(ResourceDTO? resourceDTO, AppContext appContext) {
return BoxDecoration(
shape: BoxShape.rectangle,
color: kWhite,
borderRadius: BorderRadius.circular(30.0),
image: resourceDTO != null ? resourceDTO.type != null && (resourceDTO.type == ResourceType.Image || resourceDTO.type == ResourceType.ImageUrl)? new DecorationImage(
fit: widget.imageFit,
image: new NetworkImage(
resourceDTO.url!, // TODO handle multiple type of content
),
) : null : null,
boxShadow: [
BoxShadow(
spreadRadius: 0.5,
blurRadius: 1,
offset: Offset(0, 1.5), // changes position of shadow
),
],
);
}
}

View File

@ -2,19 +2,19 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/upload_image_container.dart';
import 'package:manager_app/Components/upload_content_container.dart';
import 'package:manager_app/Components/upload_online_resources_container.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
class ResourceTab extends StatefulWidget {
final ResourceDTO resourceDTO;
final Function onFileUpload;
final Function onFileUploadWeb;
const ResourceTab({
Key key,
this.resourceDTO,
this.onFileUpload,
this.onFileUploadWeb,
Key? key,
required this.resourceDTO,
required this.onFileUpload,
required this.onFileUploadWeb,
}) : super(key: key);
@override
@ -22,17 +22,16 @@ class ResourceTab extends StatefulWidget {
}
class _ResourceTabState extends State<ResourceTab> with SingleTickerProviderStateMixin {
TabController _tabController;
List<Tab> tabsToShow = new List<Tab>();
TabController? _tabController;
List<Tab> tabsToShow = <Tab>[];
@override
void initState() {
tabsToShow.add(new Tab(text: "Image local"));
tabsToShow.add(new Tab(text: "Image en ligne"));
tabsToShow.add(new Tab(text: "Vidéo en ligne"));
tabsToShow.add(new Tab(text: "Local"));
tabsToShow.add(new Tab(text: "En ligne"));
_tabController = new TabController(length: 3, vsync: this);
_tabController.addListener(_handleTabSelection);
_tabController = new TabController(length: 2, vsync: this);
//_tabController!.addListener(_handleTabSelection);
super.initState();
}
@ -64,45 +63,45 @@ class _ResourceTabState extends State<ResourceTab> with SingleTickerProviderStat
);
}
void _handleTabSelection() {
switch(_tabController.index) {
/*void _handleTabSelection() {
switch(_tabController!.index) {
case 0:
setState(() {
widget.resourceDTO.data = null;
widget.resourceDTO.type = ResourceType.image;
widget.resourceDTO.url = null;
widget.resourceDTO.type = ResourceType.Image;
});
break;
case 1:
setState(() {
widget.resourceDTO.data = null;
widget.resourceDTO.type = ResourceType.imageUrl;
widget.resourceDTO.url = null;
widget.resourceDTO.type = ResourceType.ImageUrl;
});
break;
case 2:
setState(() {
widget.resourceDTO.data = null;
widget.resourceDTO.type = ResourceType.videoUrl;
widget.resourceDTO.url = null;
widget.resourceDTO.type = ResourceType.Audio;
});
break;
}
}
}*/
}
getContent(ResourceDTO resourceDTO, Function onFileUpload, Function onFileUploadWeb) {
List<Widget> tabsToShow = new List<Widget>();
List<Widget> tabsToShow = <Widget>[];
// Local Image
tabsToShow.add(
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: UploadImageContainer(
onChanged: (List<File> files) {
child: UploadContentContainer(
onChanged: (List<File>? files) {
onFileUpload(files);
resourceDTO.type = ResourceType.image;
resourceDTO.type = ResourceType.Image;
},
onChangedWeb: (List<PlatformFile> files) {
onChangedWeb: (List<PlatformFile>? files) {
onFileUploadWeb(files);
resourceDTO.type = ResourceType.image;
resourceDTO.type = ResourceType.Image;
},
),
)
@ -121,6 +120,40 @@ getContent(ResourceDTO resourceDTO, Function onFileUpload, Function onFileUpload
)
);
// Audio
/*tabsToShow.add(
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: UploadAudioContainer(
onChanged: (List<File> files) {
onFileUpload(files);
resourceDTO.type = ResourceType.Audio;
},
onChangedWeb: (List<PlatformFile> files) {
onFileUploadWeb(files);
resourceDTO.type = ResourceType.Audio;
},
),
)
);
// Video
tabsToShow.add(
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: UploadContentContainer(
onChanged: (List<File>? files) {
onFileUpload(files);
resourceDTO.type = ResourceType.Video;
},
onChangedWeb: (List<PlatformFile>? files) {
onFileUploadWeb(files);
resourceDTO.type = ResourceType.Video;
},
),
)
);
// Online Video
tabsToShow.add(
new Padding(
@ -133,5 +166,39 @@ getContent(ResourceDTO resourceDTO, Function onFileUpload, Function onFileUpload
),
)
);
// PDF
tabsToShow.add(
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: UploadContentContainer(
onChanged: (List<File>? files) {
onFileUpload(files);
resourceDTO.type = ResourceType.Pdf;
},
onChangedWeb: (List<PlatformFile>? files) {
onFileUploadWeb(files);
resourceDTO.type = ResourceType.Pdf;
},
),
)
);
// JSON
tabsToShow.add(
new Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: UploadContentContainer(
onChanged: (List<File>? files) {
onFileUpload(files);
resourceDTO.type = ResourceType.Json;
},
onChangedWeb: (List<PlatformFile>? files) {
onFileUploadWeb(files);
resourceDTO.type = ResourceType.Json;
},
),
)
);*/
return tabsToShow;
}

View File

@ -1,23 +1,24 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
class RoundedButton extends StatelessWidget {
final String text;
final Function press;
final IconData icon;
final IconData? icon;
final Color color, textColor;
final double fontSize;
final int vertical;
final int horizontal;
final double? vertical;
final double? horizontal;
const RoundedButton({
Key key,
this.text,
this.press,
Key? key,
required this.text,
required this.press,
this.icon,
this.color = kPrimaryColor,
this.textColor = kWhite,
this.fontSize,
required this.fontSize,
this.vertical,
this.horizontal
}) : super(key: key);
@ -27,7 +28,7 @@ class RoundedButton extends StatelessWidget {
//Size size = MediaQuery.of(context).size;
return TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.resolveWith((states) => EdgeInsets.symmetric(vertical: this.vertical != null ? this.vertical : 25, horizontal: this.horizontal != null ? this.horizontal : (icon == null ? 85 : 30))),
padding: MaterialStateProperty.resolveWith((states) => EdgeInsets.symmetric(vertical: this.vertical != null ? this.vertical! : 25, horizontal: this.horizontal != null ? this.horizontal!: (icon == null ? 85 : 30))),
backgroundColor: MaterialStateColor.resolveWith((states) => color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
@ -49,9 +50,12 @@ class RoundedButton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Center(
child: Text(
child: AutoSizeText(
text,
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
),
),
SizedBox(
@ -65,9 +69,11 @@ class RoundedButton extends StatelessWidget {
],
);
} else {
return Text(
return AutoSizeText(
text,
style: new TextStyle(color: textColor, fontSize: fontSize, fontWeight: FontWeight.w400),
maxLines: 2,
textAlign: TextAlign.center,
);
}
}

View File

@ -1,26 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:manager_app/Components/text_field_container.dart';
import 'package:manager_app/constants.dart';
class RoundedInputField extends StatelessWidget {
final String hintText;
final IconData icon;
final String? hintText;
final IconData? icon;
final ValueChanged<String> onChanged;
final String initialValue;
final String? initialValue;
final Color color, textColor, iconColor;
final int maxLength;
final int? maxLength;
final bool isEmail;
final double fontSize;
final String? autofill;
final bool isInt;
const RoundedInputField({
Key key,
Key? key,
this.hintText,
this.initialValue,
this.icon,
this.color = kSecond,
this.textColor = kBlack,
this.iconColor = kPrimaryColor,
this.onChanged,
required this.onChanged,
this.maxLength, // 50
this.isEmail = false
this.isEmail = false,
this.fontSize = 20,
this.autofill,
this.isInt = false
}) : super(key: key);
@override
@ -32,15 +39,19 @@ class RoundedInputField extends StatelessWidget {
initialValue: initialValue,
cursorColor: textColor,
maxLength: maxLength,
keyboardType: isEmail ? TextInputType.emailAddress : TextInputType.text,
style: TextStyle(fontSize: 20, color: textColor),
autofillHints: autofill != null ? [autofill!] : [],
keyboardType: isEmail ? TextInputType.emailAddress : isInt ? TextInputType.number : TextInputType.text,
inputFormatters: !isInt ? [] : <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: TextStyle(fontSize: fontSize, color: textColor),
decoration: InputDecoration(
icon: icon != null ? Icon(
icon,
color: iconColor,
): null,
hintText: hintText,
hintStyle: TextStyle(fontSize: 20.0, color: textColor),
hintStyle: TextStyle(fontSize: fontSize, color: textColor, fontWeight: FontWeight.w400),
border: InputBorder.none,
)
),

View File

@ -6,9 +6,9 @@ class RoundedPasswordField extends StatefulWidget {
final ValueChanged<String> onChanged;
final String initialValue;
const RoundedPasswordField({
Key key,
this.onChanged,
this.initialValue,
Key? key,
required this.onChanged,
required this.initialValue
}) : super(key: key);
@override
@ -25,11 +25,12 @@ class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
obscureText: isVisible,
onChanged: widget.onChanged,
initialValue: widget.initialValue,
autofillHints: [AutofillHints.password],
cursorColor: kPrimaryColor,
style: TextStyle(fontSize: 20, color: kBlack),
decoration: InputDecoration(
hintText: "Password",
hintStyle: TextStyle(fontSize: 20.0, color: kBlack),
hintText: "Mot de passe",
hintStyle: TextStyle(fontSize: 20.0, color: kBlack, fontWeight: FontWeight.normal),
icon: Icon(
Icons.lock,
color: kPrimaryColor,

View File

@ -0,0 +1,89 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
class SingleSelectContainer extends StatefulWidget {
final Color? color;
final String label;
final String? initialValue;
final List<String> inputValues;
final ValueChanged<String>? onChanged;
final double fontSize;
final double fontSizeText;
const SingleSelectContainer({
Key? key,
this.color = kSecond,
required this.label,
this.initialValue = "",
required this.inputValues,
this.onChanged,
this.fontSize = 25,
this.fontSizeText = 20,
}) : super(key: key);
@override
_SingleSelectContainerState createState() => _SingleSelectContainerState();
}
class _SingleSelectContainerState extends State<SingleSelectContainer> {
String? selectedValue;
@override
void initState() {
selectedValue = widget.initialValue;
super.initState();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: AutoSizeText(
widget.label,
style: TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: widget.fontSize,
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: size.width *0.15,
constraints: BoxConstraints(maxWidth: 175),
child: DropdownButton<String>(
value: selectedValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: widget.color!),
underline: Container(
height: 2,
color: kPrimaryColor,
),
onChanged: (String? newValue) {
setState(() {
selectedValue = newValue!;
widget.onChanged!(selectedValue!);
});
},
items: widget.inputValues.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w400, color: widget.color)),
);
}).toList(),
),
),
),
],
),
);
}
}

View File

@ -9,13 +9,13 @@ class SliderInputContainer extends StatefulWidget {
final int max;
final ValueChanged<double> onChanged;
const SliderInputContainer({
Key key,
Key? key,
this.color = kSecond,
this.label,
this.initialValue,
this.min,
this.max,
this.onChanged,
required this.label,
required this.initialValue,
required this.min,
required this.max,
required this.onChanged,
}) : super(key: key);
@override
@ -23,7 +23,7 @@ class SliderInputContainer extends StatefulWidget {
}
class _SliderInputContainerState extends State<SliderInputContainer> {
double currentValue;
double? currentValue;
@override
void initState() {
@ -42,8 +42,11 @@ class _SliderInputContainerState extends State<SliderInputContainer> {
),
Padding(
padding: const EdgeInsets.all(10.0),
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always),
child: Slider(
value: currentValue,
value: currentValue!,
onChanged: (value) {
setState(() => currentValue = value);
widget.onChanged(value);
@ -55,6 +58,7 @@ class _SliderInputContainerState extends State<SliderInputContainer> {
activeColor: widget.color,
),
),
),
],
),
);

View File

@ -1,43 +1,56 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:manager_app/Components/rounded_input_field.dart';
import 'package:manager_app/constants.dart';
class StringInputContainer extends StatelessWidget {
final Color color;
final Color? color;
final String label;
final String initialValue;
final String? initialValue;
final ValueChanged<String> onChanged;
final bool isUrl;
final bool isSmall;
final int maxLength;
final double fontSize;
final double fontSizeText;
const StringInputContainer({
Key key,
Key? key,
this.color = kSecond,
this.label,
this.initialValue,
this.onChanged,
required this.label,
this.initialValue = "",
required this.onChanged,
this.isUrl = false,
this.isSmall = false,
this.maxLength = 50,
this.fontSize = 25,
this.fontSizeText = 20,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Row(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(label, style: TextStyle(fontSize: 25, fontWeight: FontWeight.w300))
child: AutoSizeText(
label,
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w300),
maxLines: 2,
maxFontSize: fontSize,
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
padding: const EdgeInsets.all(8.0),
child: Container(
width: isUrl ? size.width *0.6 : isSmall ? size.width *0.1 : size.width *0.2,
width: isUrl ? size.width *0.6 : isSmall ? size.width *0.1 : size.width *0.25,
child: RoundedInputField(
color: color,
color: color!,
textColor: kBlack,
fontSize: fontSizeText,
initialValue: initialValue,
onChanged: onChanged,
maxLength: maxLength,

View File

@ -5,8 +5,8 @@ class TextFieldContainer extends StatelessWidget {
final Widget child;
final Color color;
const TextFieldContainer({
Key key,
this.child,
Key? key,
required this.child,
this.color = kSecond,
}) : super(key: key);

View File

@ -9,13 +9,13 @@ class TextFormInputContainer extends StatelessWidget {
final int maxLines;
final ValueChanged<String> onChanged;
const TextFormInputContainer({
Key key,
Key? key,
this.color = kSecond,
this.label,
this.initialValue,
this.isTitle,
required this.label,
required this.initialValue,
required this.isTitle,
this.maxLines = 5,
this.onChanged
required this.onChanged
}) : super(key: key);
@override

View File

@ -0,0 +1,255 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/audio_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:provider/provider.dart';
import 'package:quill_html_editor/quill_html_editor.dart';
import 'flag_decoration.dart';
class _TranslationInputAndResourceContainerState extends State<TranslationInputAndResourceContainer> with TickerProviderStateMixin {
TabController? _tabController;
QuillEditorController controllerQuill = QuillEditorController();
ValueNotifier<String?>? currentLanguage;
bool isInit = false;
@override
void initState() {
super.initState();
_tabController = new TabController(length: widget.newValues.length, vsync: this);
currentLanguage = ValueNotifier<String>(widget.newValues.first.language!);
controllerQuill.onEditorLoaded(() {
print("onEditorLoaded");
isInit = true;
});
Future.delayed(Duration(milliseconds: 500), () {
print("Future.delayed");
controllerQuill.clear();
controllerQuill.insertText(widget.newValues[_tabController!.index].value!);
isInit = true;
});
controllerQuill.onTextChanged((p0) async {
var plainText = await controllerQuill.getPlainText();
if(widget.isTitle) {
if(plainText.length > kTitleMaxLength) {
print("to much text au dessus");
controllerQuill.undo();
}
} else {
if(plainText.length > kDescriptionMaxLength) {
print("to much text description au dessus");
controllerQuill.undo();
}
}
});
_tabController!.addListener(() {
if (!_tabController!.indexIsChanging) {
setState(() {
currentLanguage!.value = widget.newValues[_tabController!.index].language;
//if(widget.resourceTypes == null) {
print("insert try without ress");
print(widget.newValues[_tabController!.index].value!);
controllerQuill.clear();
controllerQuill.insertText(widget.newValues[_tabController!.index].value!);
//}
});
}
});
}
@override
void dispose() {
super.dispose();
_tabController!.dispose();
}
@override
Widget build(BuildContext context) {
final customToolBarList = widget.isTitle ? [
ToolBarStyle.bold,
ToolBarStyle.italic,
ToolBarStyle.color,
ToolBarStyle.background,
ToolBarStyle.clean
] : [
ToolBarStyle.bold,
ToolBarStyle.italic,
ToolBarStyle.color,
ToolBarStyle.background,
ToolBarStyle.listBullet,
ToolBarStyle.listOrdered,
ToolBarStyle.clean
];
return Container(
height: widget.isTitle ? MediaQuery.of(context).size.height *0.45 : MediaQuery.of(context).size.height *0.5,
//color: Colors.orange,
width: MediaQuery.of(context).size.width *0.7,
constraints: BoxConstraints(
minHeight: 300,
minWidth: 300
),
child: DefaultTabController(
length: widget.newValues.length,
child: Column(
children: [
RotatedBox(
quarterTurns: 0, // Can be used to test vertical tab in case of smaller screen
child: TabBar(
indicatorColor: kPrimaryColor,
//overlayColor: MaterialStateProperty().c,
labelColor: kPrimaryColor,
unselectedLabelColor: Colors.black,
controller: _tabController,
tabs: widget.newValues.map((v) => Tab(icon: FlagDecoration(language: v.language!))).toList(), // text: v.language!.toUpperCase(),
),
),
getTranslation(context, Provider.of<AppContext>(context), controllerQuill, customToolBarList, widget.isTitle, widget.resourceTypes, widget.newValues, currentLanguage!)
],
),
),
);
}
getTranslation(BuildContext context, AppContext appContext, QuillEditorController controllerQuill, List<ToolBarStyle> customToolBarList, bool isTitle, List<ResourceType>? resourceTypes, List<TranslationAndResourceDTO> newValues, ValueNotifier<String?> currentLanguage) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Container(
width: MediaQuery.of(context).size.width *0.7,
//color: Colors.blueAccent,
height: widget.isTitle ? MediaQuery.of(context).size.height *0.34 : MediaQuery.of(context).size.height *0.37,
child: resourceTypes != null ?
Column(
children: [
ToolBar(
toolBarColor: kSecond,
activeIconColor: kPrimaryColor,
padding: const EdgeInsets.all(8),
iconSize: 20,
toolBarConfig: customToolBarList,
controller: controllerQuill,
customButtons: [],
),
SingleChildScrollView(
child: Column(
children: [
Container(
height: widget.isTitle ? MediaQuery.of(context).size.height *0.13 : MediaQuery.of(context).size.height *0.2,
child: QuillHtmlEditor(
text: newValues.where((element) => element.language! == currentLanguage.value).first.value!,
hintText: '',
controller: controllerQuill,
minHeight: widget.isTitle ? 80 : 240,
/*textStyle: _editorTextStyle,
hintTextStyle: _hintTextStyle,*/
hintTextAlign: TextAlign.start,
padding: const EdgeInsets.only(left: 10, right: 10, top: 5),
hintTextPadding: EdgeInsets.zero,
backgroundColor: kBackgroundColor,
ensureVisible: true,
inputAction: widget.isTitle ? InputAction.send : InputAction.newline, // don't accept enter if title
//onFocusChanged: (hasFocus) => debugPrint('has focus $hasFocus'),
//onTextChanged: (text) => debugPrint('widget text change $text'),
onTextChanged: (value) {
if(isInit) {
newValues.where((element) => element.language! == currentLanguage.value).first.value = value;
}
},
onEditorCreated: () => debugPrint('Editor has been loaded'),
onEditorResized: (height) =>
debugPrint('Editor resized $height'),
onSelectionChanged: (sel) =>
debugPrint('${sel.index},${sel.length}'),
),
),
ValueListenableBuilder<String?>(
valueListenable: currentLanguage,
builder: (context, value, _) {
return ResourceInputContainer(
label: "Ressource à afficher :",
initialValue: newValues.where((element) => element.language! == value).first.resourceId,
color: kPrimaryColor,
inResourceTypes: resourceTypes,
isLanguageTab: true,
onChanged: (ResourceDTO resource) {
setState(() {
if(resource.id == null) {
newValues.where((element) => element.language! == value).first.resourceId = null;
newValues.where((element) => element.language! == value).first.resourceUrl = null;
newValues.where((element) => element.language! == value).first.resourceType = null;
} else {
newValues.where((element) => element.language! == value).first.resourceId = resource.id;
newValues.where((element) => element.language! == value).first.resourceUrl = resource.url;
newValues.where((element) => element.language! == value).first.resourceType = resource.type;
}
});
},
isSmall: true
);
}
),
],
),
),
],
) :
Container(
width: 250,
height: 120,
child: ValueListenableBuilder<String?>(
valueListenable: currentLanguage,
builder: (context, value, _) {
return ResourceInputContainer(
label: "",
initialValue: newValues.where((element) => element.language! == value).first.resourceId,
inResourceTypes: widget.resourceTypes!,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language! == value).first.value = resource.id;
}
);
return AudioInputContainer(
initialValue: newValues.where((element) => element.language! == value).first.value,
color: kPrimaryColor,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language! == value).first.value = resource.id;
},
);
}
),
),
),
),
);
}
}
class TranslationInputAndResourceContainer extends StatefulWidget {
TranslationInputAndResourceContainer({
Key? key,
required this.isTitle,
required this.values,
required this.newValues,
required this.onGetResult,
required this.maxLines,
required this.resourceTypes,
}) : super(key: key);
bool isTitle;
List<TranslationAndResourceDTO> values;
List<TranslationAndResourceDTO> newValues;
Function onGetResult;
int maxLines;
List<ResourceType>? resourceTypes;
@override
State<TranslationInputAndResourceContainer> createState() => _TranslationInputAndResourceContainerState();
}

View File

@ -0,0 +1,253 @@
import 'package:flutter/material.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/Components/audio_input_container.dart';
import 'package:manager_app/Components/resource_input_container.dart';
import 'package:manager_app/app_context.dart';
import 'package:manager_app/constants.dart';
import 'package:provider/provider.dart';
import 'package:quill_html_editor/quill_html_editor.dart';
import 'flag_decoration.dart';
class _TranslationInputContainerState extends State<TranslationInputContainer> with TickerProviderStateMixin {
TabController? _tabController;
QuillEditorController controllerQuill = QuillEditorController();
ValueNotifier<String?>? currentLanguage;
bool isInit = false;
@override
void initState() {
super.initState();
_tabController = new TabController(length: widget.newValues.length, vsync: this);
currentLanguage = ValueNotifier<String>(widget.newValues.first.language!);
controllerQuill.onEditorLoaded(() {
print("onEditorLoaded");
isInit = true;
});
Future.delayed(Duration(milliseconds: 500), () {
print("Future.delayed");
controllerQuill.clear();
controllerQuill.insertText(widget.newValues[_tabController!.index].value!);
isInit = true;
});
controllerQuill.onTextChanged((p0) async {
var plainText = await controllerQuill.getPlainText();
if(widget.isTitle) {
if(plainText.length > kTitleMaxLength) {
print("to much text au dessus");
controllerQuill.undo();
}
} else {
if(plainText.length > kDescriptionMaxLength) {
print("to much text description au dessus");
controllerQuill.undo();
}
}
});
_tabController!.addListener(() {
if (!_tabController!.indexIsChanging) {
setState(() {
currentLanguage!.value = widget.newValues[_tabController!.index].language;
if(widget.resourceTypes == null) {
print("insert try without ress");
print(widget.newValues[_tabController!.index].value!);
controllerQuill.clear();
controllerQuill.insertText(widget.newValues[_tabController!.index].value!);
}
});
}
});
}
@override
void dispose() {
super.dispose();
_tabController!.dispose();
}
getTranslation(BuildContext context, AppContext appContext, QuillEditorController controllerQuill, List<ToolBarStyle> customToolBarList, bool isTitle, List<ResourceType>? resourceTypes, List<TranslationDTO> newValues, ValueNotifier<String?> currentLanguage) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Container(
width: MediaQuery.of(context).size.width *0.7,
height: widget.isTitle ? MediaQuery.of(context).size.height *0.25 : MediaQuery.of(context).size.height *0.4,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
resourceTypes == null ?
Column(
children: [
ToolBar(
toolBarColor: kSecond,
activeIconColor: kPrimaryColor,
padding: const EdgeInsets.all(8),
iconSize: 20,
toolBarConfig: customToolBarList,
controller: controllerQuill,
customButtons: [],
),
SingleChildScrollView(
child: Container(
height: widget.isTitle ? MediaQuery.of(context).size.height *0.13 : MediaQuery.of(context).size.height *0.35,
child: QuillHtmlEditor(
//text: newValues.where((element) => element.language! == currentLanguage.value).first.value!,
hintText: '',
controller: controllerQuill,
minHeight: widget.isTitle ? 80 : 240,
/*textStyle: _editorTextStyle,
hintTextStyle: _hintTextStyle,*/
hintTextAlign: TextAlign.start,
padding: const EdgeInsets.only(left: 10, right: 10, top: 5),
hintTextPadding: EdgeInsets.zero,
backgroundColor: kBackgroundColor,
ensureVisible: true,
inputAction: widget.isTitle ? InputAction.send : InputAction.newline, // don't accept enter if title
//onFocusChanged: (hasFocus) => debugPrint('has focus $hasFocus'),
//onTextChanged: (text) => debugPrint('widget text change $text'),
onTextChanged: (value) {
if(isInit) {
newValues.where((element) => element.language! == currentLanguage.value).first.value = value;
}
},
onEditorCreated: () => debugPrint('Editor has been loaded'),
onEditorResized: (height) =>
debugPrint('Editor resized $height'),
onSelectionChanged: (sel) =>
debugPrint('${sel.index},${sel.length}'),
),
),
),
],
)
/*HtmlEditor(
controller: controller,
htmlEditorOptions: HtmlEditorOptions(
hint: "Your text here...",
initialText: newValues.where((element) => element.language == language).first.value!,
shouldEnsureVisible: true,
),
htmlToolbarOptions: HtmlToolbarOptions(
toolbarPosition: ToolbarPosition.aboveEditor, //required to place toolbar anywhere!
//other options
),
otherOptions: OtherOptions(
height: 400,
),
)*/
/*TextFormInputContainer(
label: label,
color: kWhite,
isTitle: isTitle,
initialValue: newValues.where((element) => element.language == language).first.value!,
onChanged: (value) {
newValues.where((element) => element.language == language).first.value = value;
},
)*/ :
Container(
width: 250,
height: 120,
child: ValueListenableBuilder<String?>(
valueListenable: currentLanguage,
builder: (context, value, _) {
return ResourceInputContainer(
label: "",
initialValue: newValues.where((element) => element.language! == value).first.value,
inResourceTypes: widget.resourceTypes!,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language! == value).first.value = resource.id;
}
);
/*return AudioInputContainer(
//label: "Audio :",
initialValue: newValues.where((element) => element.language! == value).first.value,
color: kPrimaryColor,
onChanged: (ResourceDTO resource) {
newValues.where((element) => element.language! == value).first.value = resource.id;
},
);*/
},
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
final customToolBarList = widget.isTitle ? [
ToolBarStyle.bold,
ToolBarStyle.italic,
ToolBarStyle.color,
ToolBarStyle.background,
ToolBarStyle.clean
] : [
ToolBarStyle.bold,
ToolBarStyle.italic,
ToolBarStyle.color,
ToolBarStyle.background,
ToolBarStyle.listBullet,
ToolBarStyle.listOrdered,
ToolBarStyle.clean
];
return Container(
height: widget.isTitle ? MediaQuery.of(context).size.height *0.35 : MediaQuery.of(context).size.height *0.53,
width: MediaQuery.of(context).size.width *0.7,
constraints: BoxConstraints(
minHeight: 200,
minWidth: 300
),
child: DefaultTabController(
length: widget.newValues.length,
child: Column(
children: [
RotatedBox(
quarterTurns: 0, // Can be used to test vertical tab in case of smaller screen
child: TabBar(
indicatorColor: kPrimaryColor,
//overlayColor: MaterialStateProperty().c,
labelColor: kPrimaryColor,
unselectedLabelColor: Colors.black,
controller: _tabController,
tabs: widget.newValues.map((v) => Tab(icon: FlagDecoration(language: v.language!))).toList(), // text: v.language!.toUpperCase(),
),
),
getTranslation(context, Provider.of<AppContext>(context), controllerQuill, customToolBarList, widget.isTitle, widget.resourceTypes, widget.newValues, currentLanguage!)
],
),
),
);
}
}
class TranslationInputContainer extends StatefulWidget {
TranslationInputContainer({
Key? key,
required this.isTitle,
required this.values,
required this.newValues,
required this.onGetResult,
required this.maxLines,
required this.resourceTypes,
}) : super(key: key);
bool isTitle;
List<TranslationDTO> values;
List<TranslationDTO> newValues;
Function onGetResult;
int maxLines;
List<ResourceType>? resourceTypes;
@override
State<TranslationInputContainer> createState() => _TranslationInputContainerState();
}

View File

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
class TranslationTab extends StatefulWidget {
final List<TranslationDTO> translations;
final int maxLines;
const TranslationTab({
Key key,
this.translations,
this.maxLines,
Key? key,
required this.translations,
required this.maxLines,
}) : super(key: key);
@override
@ -15,7 +15,7 @@ class TranslationTab extends StatefulWidget {
}
class _TranslationTabState extends State<TranslationTab> with SingleTickerProviderStateMixin {
TabController _tabController;
TabController? _tabController;
@override
void initState() {
@ -52,7 +52,7 @@ class _TranslationTabState extends State<TranslationTab> with SingleTickerProvid
}
getContent(List<TranslationDTO> translations, int maxLines) {
List<Widget> tabsToShow = new List<Widget>();
List<Widget> tabsToShow = <Widget>[];
translations.forEach((translation) {
tabsToShow.add(
new Padding(
@ -64,8 +64,8 @@ getContent(List<TranslationDTO> translations, int maxLines) {
maxLines: maxLines,
initialValue: translation.value,
onChanged: (String value) {
print("onChanged value in tranbslationTAB");
print(value);
//print("onChanged value in tranbslationTAB");
//print(value);
translation.value = value;
},
cursorColor: kPrimaryColor,
@ -103,7 +103,7 @@ getContent(List<TranslationDTO> translations, int maxLines) {
}
getTabs(List<TranslationDTO> newValues) {
List<Tab> tabsToShow = new List<Tab>();
List<Tab> tabsToShow = <Tab>[];
newValues.forEach((value) {
tabsToShow.add(
new Tab(text: value.language));

View File

@ -1,28 +1,26 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:manager_app/Components/loading.dart';
import 'package:manager_app/constants.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class UploadImageContainer extends StatefulWidget {
class UploadAudioContainer extends StatefulWidget {
final ValueChanged<List<File>> onChanged;
final ValueChanged<List<PlatformFile>> onChangedWeb;
const UploadImageContainer({
Key key,
this.onChanged,
this.onChangedWeb,
const UploadAudioContainer({
Key? key,
required this.onChanged,
required this.onChangedWeb,
}) : super(key: key);
@override
_UploadImageContainerState createState() => _UploadImageContainerState();
_UploadAudioContainerState createState() => _UploadAudioContainerState();
}
class _UploadImageContainerState extends State<UploadImageContainer> with SingleTickerProviderStateMixin {
class _UploadAudioContainerState extends State<UploadAudioContainer> with SingleTickerProviderStateMixin {
var filePath;
File fileToShow;
String fileToShowWeb;
File? fileToShow;
String? fileToShowWeb;
@override
void initState() {
@ -44,13 +42,13 @@ class _UploadImageContainerState extends State<UploadImageContainer> with Single
}
Future<void> filePicker() async {
FilePickerResult result;
FilePickerResult? result;
if (kIsWeb) {
result = await FilePicker.platform.pickFiles(
type: FileType.custom,
dialogTitle: 'Sélectionner un fichier',
dialogTitle: 'Sélectionner un fichier audio mp3',
allowMultiple: true,
allowedExtensions: ['jpg', 'jpeg', 'png'],
allowedExtensions: ['mp3'],
);
if (result != null) {
@ -65,12 +63,13 @@ class _UploadImageContainerState extends State<UploadImageContainer> with Single
} else {
result = await FilePicker.platform.pickFiles(
type: FileType.custom,
dialogTitle: 'Sélectionner un fichier',
dialogTitle: 'Sélectionner un fichier audio mp3',
allowMultiple: true,
allowedExtensions: ['jpg', 'jpeg', 'png'],
allowedExtensions: ['mp3'],
);
List<File> files = result.paths.map((path) => File(path)).toList();
if(result != null) {
List<File> files = result.paths.map((path) => File(path!)).toList();
var file = files[0];
setState(() {
filePath = file.path; // Only show one picture
@ -78,6 +77,7 @@ class _UploadImageContainerState extends State<UploadImageContainer> with Single
widget.onChanged(files);
});
}
}
/*final file = OpenFilePicker()
..filterSpecification = {
'Images (*.jpg; *.jpeg;*.png)': '*.jpg;*.jpeg;*.png',
@ -128,7 +128,7 @@ class _UploadImageContainerState extends State<UploadImageContainer> with Single
return Center(
child: Container(
height: 200,
child: Loading()
child: LoadingCommon()
)
);
}
@ -138,11 +138,11 @@ class _UploadImageContainerState extends State<UploadImageContainer> with Single
if (kIsWeb) {
return null;
} else {
return Image.file(
return Text("TODO player affiché ou pas ?");/*Image.file(
fileToShow,
height: 200,
fit:BoxFit.scaleDown
);
);*/
}
}
}

View File

@ -0,0 +1,209 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:manager_app/constants.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
class UploadContentContainer extends StatefulWidget {
final ValueChanged<List<File>?> onChanged;
final ValueChanged<List<PlatformFile>?> onChangedWeb;
const UploadContentContainer({
Key? key,
required this.onChanged,
required this.onChangedWeb,
}) : super(key: key);
@override
_UploadContentContainerState createState() => _UploadContentContainerState();
}
class _UploadContentContainerState extends State<UploadContentContainer> with SingleTickerProviderStateMixin {
var filePath;
File? fileToShow;
String? fileToShowWeb;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
width: size.width *0.5,
height: size.height *0.5,
child: displayElement(),
);
}
String getFileName(filePath) {
return (filePath.toString().split('\\').last);
}
Future<void> filePicker() async {
FilePickerResult? result;
if (kIsWeb) {
result = await FilePicker.platform.pickFiles(
type: FileType.custom,
dialogTitle: 'Sélectionner un fichier',
allowMultiple: true,
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'mp3', 'mp4', 'webm', 'pdf', 'json'],
);
if (result != null) {
List<PlatformFile> files = result.files;
setState(() {
filePath = "Fichiers"; // Only show one picture
fileToShowWeb = "Aucun aperçu possible"; // Only show one picture
widget.onChangedWeb(files);
});
}
} else {
result = await FilePicker.platform.pickFiles(
type: FileType.custom,
dialogTitle: 'Sélectionner un fichier',
allowMultiple: true,
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'mp3', 'mp4', 'webm', 'pdf', 'json'],
);
if (result != null) {
List<File> files = result.paths.map((path) => File(path!)).toList();
var file = files[0];
setState(() {
filePath = file.path; // Only show one picture
fileToShow = file; // Only show one picture
widget.onChanged(files);
});
}
}
/*final file = OpenFilePicker()
..filterSpecification = {
'Images (*.jpg; *.jpeg;*.png)': '*.jpg;*.jpeg;*.png',
//'Video (*.mp4)': '*.mp4',
//'All Files': '*.*'
}
..defaultFilterIndex = 0
..title = 'Sélectionner un fichier';
final result = file.getFile();
if (result != null) {
setState(() {
filePath = result.path;
fileToShow = result;
widget.onChanged(result);
});
}*/
}
showFile() {
if (getFileName(filePath).contains(".mp4")) {
/*return FutureBuilder(
future: loadFile(fileToShow),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Container(
height: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("La prévisualisation de la vidéo n'est pas disponible"),
),
Icon(
Icons.ondemand_video_sharp,
size: 35,
),
],
),
//DartVLC(file: snapshot.data)
);
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else {
return Center(
child: Container(
height: 200,
child: LoadingCommon()
)
);
}
}
);*/
} else {
if (kIsWeb) {
return null;
} else {
if(fileToShow != null) {
// depends on type..
return Image.file(
fileToShow!,
height: 200,
fit:BoxFit.scaleDown
);
}
}
}
}
loadFile(File fileToShow) async {
//return await Media.file(fileToShow);
return null; // Useless no mp4 for now
}
displayElement() {
if (fileToShow == null && fileToShowWeb == null) return Center(
child: InkWell(
onTap: () async {
filePicker();
},
child: Container(
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(15)
),
child: Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 15.0, bottom: 15.0),
child: Text(
"Ajouter un ou plusieurs fichiers",
style: new TextStyle(color: kWhite),
),
)
),
),
);
if (fileToShow != null || fileToShowWeb != null)
return Container(
margin: EdgeInsets.all(8.0),
child: Card(
color: kBackgroundColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: InkWell(
onTap: () async {
filePicker();
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
child: showFile()
),
ListTile(
title: Text(getFileName(filePath)),
subtitle: Text(filePath),
),
],
),
),
),
);
}
}

View File

@ -1,16 +1,15 @@
import 'package:flutter/cupertino.dart';
import 'package:manager_app/Components/rounded_input_field.dart';
import 'package:manager_app/constants.dart';
import 'package:managerapi/api.dart';
import 'package:manager_api_new/api.dart';
import 'package:flutter/material.dart';
class UploadOnlineResourceContainer extends StatefulWidget {
final ResourceDTO resourceDTO;
final ValueChanged<ResourceDTO> onChanged;
const UploadOnlineResourceContainer({
Key key,
this.resourceDTO,
this.onChanged,
Key? key,
required this.resourceDTO,
required this.onChanged,
}) : super(key: key);
@override
@ -18,7 +17,7 @@ class UploadOnlineResourceContainer extends StatefulWidget {
}
class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceContainer> with SingleTickerProviderStateMixin {
String urlResourceToShow;
String? urlResourceToShow;
@override
void initState() {
@ -28,12 +27,12 @@ class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceCont
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
print(size.width);
//print(size.width);
return displayElement(size);
}
showFile() {
if (widget.resourceDTO.type == ResourceType.videoUrl || widget.resourceDTO.type == ResourceType.video) {
if (widget.resourceDTO.type == ResourceType.VideoUrl || widget.resourceDTO.type == ResourceType.Video) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -65,9 +64,24 @@ class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceCont
/*await Media.file(widget.file)*/
} else {
return Image.network(
urlResourceToShow,
height: 200,
fit:BoxFit.scaleDown
urlResourceToShow!,
height: 150,
fit:BoxFit.scaleDown,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: CircularProgressIndicator(
color: kPrimaryColor,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
);
}
}
@ -91,12 +105,14 @@ class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceCont
child: Container(
width: size.width *0.35, // TODO GET SIZE
child: RoundedInputField(
hintText: widget.resourceDTO.type == ResourceType.imageUrl ? "Url de l'image" : "Url de la vidéo",
icon: widget.resourceDTO.type == ResourceType.imageUrl ? Icons.image : Icons.ondemand_video, // TODO: TBD
hintText: "Url de l'image, de la vidéo youtube ou du fichier json",
//icon: widget.resourceDTO.type == ResourceType.ImageUrl ? Icons.image : Icons.ondemand_video, // TODO: TBD
onChanged: (String text) {
print("onchanged url");
widget.resourceDTO.data = text;
widget.resourceDTO.url = text; // TODO check if ok
widget.onChanged(widget.resourceDTO);
widget.resourceDTO.type = _isYouTubeVideo(widget.resourceDTO.url!) ? ResourceType.VideoUrl : widget.resourceDTO.url!.endsWith("json") || widget.resourceDTO.url!.endsWith("php") ? ResourceType.JsonUrl : ResourceType.ImageUrl; // ou VideoUrl
print("URL OR NOOOOT ?");
print(widget.resourceDTO.type);
},
initialValue: ""
),
@ -104,9 +120,9 @@ class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceCont
),
InkWell(
onTap: () {
print("refresh preview");
//print("refresh preview");
setState(() {
urlResourceToShow = widget.resourceDTO.data;
urlResourceToShow = widget.resourceDTO.url;
});
},
child: Center(
@ -120,8 +136,17 @@ class _UploadOnlineResourceContainerState extends State<UploadOnlineResourceCont
],
),
),
if(widget.resourceDTO.data != null) showFile()
if(widget.resourceDTO.url != null) showFile()
],
);
}
bool _isYouTubeVideo(String url) {
RegExp regExp = RegExp(
r'^https?://(?:www\.)?(?:youtube\.com/(?:[^/]+/[^/]+/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be/)([^"&?/=%]{11})');
return regExp.hasMatch(url);
}
}

Some files were not shown because too many files have changed in this diff Show More