From 0574723f8158c3bac9f232e2e1d42a2a9212572c Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 4 Mar 2023 05:07:04 +0100 Subject: [PATCH 01/37] HomePageV1 --- lib/main.dart | 78 ++++++++++++++++++++++++++++++---- lib/main_page.dart | 101 +++++++++++++++++++++++++++++++++++++++++++++ lib/textstyle.dart | 19 +++++++++ pubspec.yaml | 2 +- 4 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 lib/main_page.dart create mode 100644 lib/textstyle.dart diff --git a/lib/main.dart b/lib/main.dart index e25aa1c..d213d12 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,20 +1,84 @@ import 'package:flutter/material.dart'; +import 'main_page.dart'; + void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { - const MainApp({key}); + const MainApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return const MaterialApp( - home: Scaffold( - body: Center( - child: Text('Hello World!'), - ), - ), + return MaterialApp( + debugShowCheckedModeBanner: false, //debug banner desactivation + title: 'Chat ChuisPt', + theme: + ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey)), + home: const MyHomePage(), ); } } + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key}) : super(key: key); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + var selectedIndex = 0; + + @override + Widget build(BuildContext context) { + Widget page; + switch (selectedIndex) { + case 0: + page = MainPage(); + break; + case 1: + page = const Text('Page 2'); + break; + case 2: + page = const Text('Page 3'); + break; + + default: + throw UnimplementedError('no widget for $selectedIndex'); + } + + return LayoutBuilder(builder: (context, constraints) { + return Scaffold( + body: Row(children: [ + SafeArea( + child: NavigationRail( + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.home), + label: Text('Main'), + ), + NavigationRailDestination( + icon: Icon(Icons.favorite_border), + label: Text('Favorites'), + ), + NavigationRailDestination( + icon: Icon(Icons.bookmark_border), + label: Text('Bookmarks'), + ), + ], + + // Destination selected + selectedIndex: selectedIndex, + onDestinationSelected: (value) { + setState(() { + selectedIndex = value; + }); + }, + )), + Expanded(child: page), + ])); + }); + } +} diff --git a/lib/main_page.dart b/lib/main_page.dart new file mode 100644 index 0000000..8106fa4 --- /dev/null +++ b/lib/main_page.dart @@ -0,0 +1,101 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; + +import 'textstyle.dart'; +import 'main.dart'; + +class MainPage extends StatefulWidget { + const MainPage({Key? key}) : super(key: key); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + var selectedIndex = 0; + + @override + Widget build(BuildContext context) { + return Column(children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 50), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 100), + const Flexible(child: QuestionGrid()), + const Center( + child: Padding( + padding: EdgeInsets.only(bottom: 75, right: 75), + child: TextField( + style: TextStyle(color: Colors.black, fontSize: 30), + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Posez votre question ici...', + ), + ), + )), + ]); + } +} + +class QuestionGrid extends StatelessWidget { + const QuestionGrid({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + List questions = []; + for (int i = 1; i <= 9; i++) { + questions.add( + Container( + color: Colors.grey[300], + child: TextButton( + onPressed: onPressed, + child: Text('$i', style: titleText2), + ), + ), + ); + } + + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Center( + child: IconButton( + onPressed: onPressed, + icon: Icon(Icons.favorite), + ), + ), + Center( + child: IconButton( + onPressed: onPressed, + icon: Icon(Icons.home), + ), + ), + Center( + child: IconButton( + onPressed: onPressed, + icon: Icon(Icons.work), + ), + ), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 350, right: 350), + child: GridView.count( + primary: false, + crossAxisSpacing: 75, + mainAxisSpacing: 30, + crossAxisCount: 3, + children: questions, + ), + ), + ), + ], + ); + } + + void onPressed() {} +} diff --git a/lib/textstyle.dart b/lib/textstyle.dart new file mode 100644 index 0000000..264c546 --- /dev/null +++ b/lib/textstyle.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +TextStyle normalText = const TextStyle( + color: Colors.black, + fontSize: 15, + fontWeight: FontWeight.w300, +); + +TextStyle titleText = const TextStyle( + color: Colors.black, + fontSize: 60, + fontWeight: FontWeight.w700, +); + +TextStyle titleText2 = const TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500, +); diff --git a/pubspec.yaml b/pubspec.yaml index cc63d33..3c2dc6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: '>=2.0.0 <4.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: flutter: From 23d8dcb48b443cbaf66503329f67df0b745bf5f7 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Sat, 4 Mar 2023 05:22:41 +0100 Subject: [PATCH 02/37] firebase_core package added --- android/app/google-services.json | 40 ++++++++++++ lib/firebase_options.dart | 63 ++++++++++++++++++ lib/main.dart | 9 ++- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 64 +++++++++++++++---- pubspec.yaml | 3 +- 6 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 lib/firebase_options.dart diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..9f56abb --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "363041976766", + "firebase_url": "https://chat-chuispt-default-rtdb.europe-west1.firebasedatabase.app", + "project_id": "chat-chuispt", + "storage_bucket": "chat-chuispt.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:363041976766:android:1fbc51100b4ad0a1cbc9ef", + "android_client_info": { + "package_name": "com.example.chat_chuispt" + } + }, + "oauth_client": [ + { + "client_id": "363041976766-bsbrl73aj9euefgqipf7qghk03d2idkf.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCGPdadzqssGTDxcVR5VTSox_jzhKeZkIg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "363041976766-bsbrl73aj9euefgqipf7qghk03d2idkf.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..7a11d78 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,63 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for ios - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyCGPdadzqssGTDxcVR5VTSox_jzhKeZkIg', + appId: '1:363041976766:android:1fbc51100b4ad0a1cbc9ef', + messagingSenderId: '363041976766', + projectId: 'chat-chuispt', + databaseURL: 'https://chat-chuispt-default-rtdb.europe-west1.firebasedatabase.app', + storageBucket: 'chat-chuispt.appspot.com', + ); +} diff --git a/lib/main.dart b/lib/main.dart index e25aa1c..19192b4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); -void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { - const MainApp({key}); + const MainApp({super.key}); @override Widget build(BuildContext context) { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..e46c39f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import firebase_core func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index b42112c..297c9e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" fake_async: dependency: transitive description: @@ -49,6 +49,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: fe30ac230f12f8836bb97e6e09197340d3c584526825b1746ea362a82e1e43f7 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55" + url: "https://pub.dev" + source: hosted + version: "4.5.3" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "291fbcace608aca6c860652e1358ef89752be8cc3ef227f8bbcd1e62775b833a" + url: "https://pub.dev" + source: hosted + version: "2.2.1" flutter: dependency: "direct main" description: flutter @@ -67,14 +91,19 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" js: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: @@ -87,10 +116,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8 url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.14" material_color_utilities: dependency: transitive description: @@ -103,18 +132,26 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" sky_engine: dependency: transitive description: flutter @@ -164,10 +201,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.4.18" vector_math: dependency: transitive description: @@ -177,4 +214,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.19.0 <4.0.0" + flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml index cc63d33..04de33c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,9 +4,10 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: '>=2.0.0 <4.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: + firebase_core: ^2.7.0 flutter: sdk: flutter From 1af338168ba143b7e6ebb31278c60589273ebbd5 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 4 Mar 2023 05:36:25 +0100 Subject: [PATCH 03/37] TextField1 --- lib/main_page.dart | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/main_page.dart b/lib/main_page.dart index 8106fa4..f4d7cc0 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -14,6 +14,10 @@ class MainPage extends StatefulWidget { class _MainPageState extends State { var selectedIndex = 0; + // store the user's question + final _textController = TextEditingController(); + String userPost = ''; + @override Widget build(BuildContext context) { return Column(children: [ @@ -23,17 +27,26 @@ class _MainPageState extends State { Text('Exemples de questions...', style: titleText2), const SizedBox(height: 100), const Flexible(child: QuestionGrid()), - const Center( + Center( child: Padding( - padding: EdgeInsets.only(bottom: 75, right: 75), + padding: const EdgeInsets.only(bottom: 75, right: 75), child: TextField( - style: TextStyle(color: Colors.black, fontSize: 30), + controller: _textController, + style: const TextStyle(color: Colors.black, fontSize: 30), decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Posez votre question ici...', - ), + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), ), )), + Text(userPost, style: titleText2), ]); } } @@ -64,26 +77,26 @@ class QuestionGrid extends StatelessWidget { Center( child: IconButton( onPressed: onPressed, - icon: Icon(Icons.favorite), + icon: const Icon(Icons.favorite), ), ), Center( child: IconButton( onPressed: onPressed, - icon: Icon(Icons.home), + icon: const Icon(Icons.home), ), ), Center( child: IconButton( onPressed: onPressed, - icon: Icon(Icons.work), + icon: const Icon(Icons.work), ), ), ], ), Expanded( child: Padding( - padding: const EdgeInsets.only(left: 350, right: 350), + padding: const EdgeInsets.only(left: 150, right: 150), child: GridView.count( primary: false, crossAxisSpacing: 75, From 450262976473851cebea7f2df1091753f87f8197 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Sat, 4 Mar 2023 05:53:43 +0100 Subject: [PATCH 04/37] Connexion to database is working ! --- android/app/build.gradle | 5 ++- android/build.gradle | 1 + lib/main.dart | 19 +++++++++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ pubspec.lock | 32 +++++++++++++++++++ pubspec.yaml | 1 + 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 2c3f56f..c86c6a8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,6 +24,7 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +apply plugin: 'com.google.gms.google-services' android { compileSdkVersion flutter.compileSdkVersion @@ -47,7 +48,7 @@ android { applicationId "com.example.chat_chuispt" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 31 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -68,4 +69,6 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation platform('com.google.firebase:firebase-bom:31.2.3') + implementation 'com.google.firebase:firebase-analytics-ktx' } diff --git a/android/build.gradle b/android/build.gradle index e50c3a0..f0d12de 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.15' } } diff --git a/lib/main.dart b/lib/main.dart index 19192b4..2d4740d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,9 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'firebase_options.dart'; +import 'package:firebase_database/firebase_database.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -12,12 +15,24 @@ void main() async { class MainApp extends StatelessWidget { const MainApp({super.key}); + void writeData() async { + DatabaseReference ref = FirebaseDatabase.instance.ref("questions/2"); + + await ref.set({ + "blue_thumb": 0, + "id": 2, + "red_thumb": 0, + "text": "Mon comptable rêve de bible et de gros chiffres." + }); + } + @override Widget build(BuildContext context) { - return const MaterialApp( + return MaterialApp( home: Scaffold( body: Center( - child: Text('Hello World!'), + child: + TextButton(onPressed: writeData, child: const Text("Write data")), ), ), ); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e46c39f..c194d13 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import firebase_core +import firebase_database func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseDatabasePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 297c9e2..7e20d8d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "64fcb0dbca4386356386c085142fa6e79c00a3326ceaa778a2d25f5d9ba61441" + url: "https://pub.dev" + source: hosted + version: "1.0.16" async: dependency: transitive description: @@ -73,6 +81,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + firebase_database: + dependency: "direct main" + description: + name: firebase_database + sha256: fdf4e9cfd22ac5e79ce188ec521d5445b39da036634cba49c7c428e08da328ec + url: "https://pub.dev" + source: hosted + version: "10.0.14" + firebase_database_platform_interface: + dependency: transitive + description: + name: firebase_database_platform_interface + sha256: cee01eb2a850466151cd13f6930eb1da65d67505c4cedc49022200eac38f2a34 + url: "https://pub.dev" + source: hosted + version: "0.2.2+21" + firebase_database_web: + dependency: transitive + description: + name: firebase_database_web + sha256: "865dc4cbb0c3d64dedfbedf425abb065174096b17ad5b7fc287eca20889235ee" + url: "https://pub.dev" + source: hosted + version: "0.2.1+23" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 04de33c..9522e9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: dependencies: firebase_core: ^2.7.0 + firebase_database: ^10.0.14 flutter: sdk: flutter From 85f0be85bd6c3709a8319586c07c96b73d018897 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Sat, 4 Mar 2023 06:57:09 +0100 Subject: [PATCH 05/37] get texts from database done --- lib/main.dart | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 2d4740d..c4dbbab 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:math'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'firebase_options.dart'; @@ -9,12 +11,10 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - runApp(const MainApp()); + runApp(MainApp()); } class MainApp extends StatelessWidget { - const MainApp({super.key}); - void writeData() async { DatabaseReference ref = FirebaseDatabase.instance.ref("questions/2"); @@ -26,13 +26,31 @@ class MainApp extends StatelessWidget { }); } + /// Get data from Firebase at questions/$id/text + /// Return a Future + Future getData(id) async { + DatabaseReference ref = FirebaseDatabase.instance.ref("questions/$id/text"); + + DatabaseEvent event = await ref.once(); + return event.snapshot.value; + } + @override Widget build(BuildContext context) { + int id = Random().nextInt(2); return MaterialApp( home: Scaffold( body: Center( - child: - TextButton(onPressed: writeData, child: const Text("Write data")), + child: FutureBuilder( + future: getData(id), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text(snapshot.data.toString()); + } else { + return const CircularProgressIndicator(); + } + }, + ), ), ), ); From 6f6b67f7dff4771ff06ca1b32abcb50a4bb00cc2 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 4 Mar 2023 08:57:48 +0100 Subject: [PATCH 06/37] Question/Answear --- lib/history_page.dart | 98 +++++++++++++++++++++++++++++++++++++++++++ lib/main.dart | 44 +++++++++++++++---- lib/main_page.dart | 9 ++-- pubspec.lock | 17 ++++++++ pubspec.yaml | 1 + 5 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 lib/history_page.dart diff --git a/lib/history_page.dart b/lib/history_page.dart new file mode 100644 index 0000000..5c2c465 --- /dev/null +++ b/lib/history_page.dart @@ -0,0 +1,98 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'main.dart'; +import 'main_page.dart'; +import 'textstyle.dart'; + +class HistoryPage extends StatefulWidget { + const HistoryPage({Key? key}) : super(key: key); + + @override + State createState() => _HistoryPageState(); +} + +class _HistoryPageState extends State { + var selectedIndex = 1; + + // store the user's question + final _textController = TextEditingController(); + String userPost = ''; + + @override + Widget build(BuildContext context) { + return Column(children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 50), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 100), + const Flexible(child: History()), + //Text(userPost, style: titleText2), + Center( + child: Padding( + padding: const EdgeInsets.only(bottom: 75, right: 75), + child: TextField( + controller: _textController, + style: const TextStyle(color: Colors.black, fontSize: 30), + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ), + )), + ]); + } +} + +class History extends StatefulWidget { + const History({Key? key}) : super(key: key); + + @override + State createState() => _HistoryState(); +} + +class _HistoryState extends State { + final _key = GlobalKey(); + List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; + + @override + Widget build(BuildContext context) { + final appState = context.watch(); + appState.historyKey = _key; + + return AnimatedList( + key: _key, + reverse: true, + padding: const EdgeInsets.only(top: 400), + initialItemCount: appState.questionsList.length, + itemBuilder: (context, index, animation) { + final question = appState.questionsList[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( + child: Column( + children: [ + const SizedBox(height: 20), + Text(question, style: titleText), + const SizedBox(height: 20), + Text(reponseList[Random().nextInt(reponseList.length)], + style: titleText.copyWith(color: Colors.red)), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index d213d12..4de9cc8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,6 @@ +import 'package:chat_chuispt/history_page.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'main_page.dart'; @@ -11,16 +13,39 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, //debug banner desactivation - title: 'Chat ChuisPt', - theme: - ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey)), - home: const MyHomePage(), + return ChangeNotifierProvider( + create: (context) => MainAppState(), + child: MaterialApp( + debugShowCheckedModeBanner: false, //debug banner desactivation + title: 'Chat ChuisPt', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey)), + home: const MyHomePage(), + ), ); } } +class MainAppState extends ChangeNotifier { + List questionsList = []; + List responsesList = []; + var historyKey = GlobalKey(); + + void addQuestion(String question) { + if (question != '') { + questionsList.insert(0, question); + notifyListeners(); + var animatedList = historyKey.currentState as AnimatedListState?; + animatedList?.insertItem(0); + } + } + + void addResponse(String response) { + responsesList.add(response); + notifyListeners(); + } +} + class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @@ -39,7 +64,7 @@ class _MyHomePageState extends State { page = MainPage(); break; case 1: - page = const Text('Page 2'); + page = HistoryPage(); break; case 2: page = const Text('Page 3'); @@ -77,7 +102,10 @@ class _MyHomePageState extends State { }); }, )), - Expanded(child: page), + Expanded( + child: Container( + //color: Theme.of(context).colorScheme.primaryContainer, + child: page)), ])); }); } diff --git a/lib/main_page.dart b/lib/main_page.dart index f4d7cc0..b3087aa 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -1,5 +1,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'textstyle.dart'; import 'main.dart'; @@ -20,6 +21,8 @@ class _MainPageState extends State { @override Widget build(BuildContext context) { + var appState = context.watch(); + return Column(children: [ const SizedBox(height: 100), Text('Chat ChuisPT', style: titleText), @@ -38,10 +41,8 @@ class _MainPageState extends State { labelText: 'Posez votre question ici...', suffixIcon: IconButton( onPressed: () { - setState(() { - userPost = _textController.text; - _textController.clear(); - }); + //appState.Send(_textController); + _textController.clear(); }, icon: const Icon(Icons.send))), ), diff --git a/pubspec.lock b/pubspec.lock index b42112c..736ce01 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -107,6 +107,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -115,6 +123,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + provider: + dependency: "direct main" + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" sky_engine: dependency: transitive description: flutter @@ -178,3 +194,4 @@ packages: version: "2.1.4" sdks: dart: ">=2.18.0 <3.0.0" + flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3c2dc6f..4b765ca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + provider: ^6.0.5 dev_dependencies: flutter_test: From bf2260859d2085ab116abc3235c5d9459f9ce42e Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Sat, 4 Mar 2023 09:14:13 +0100 Subject: [PATCH 07/37] most of back code is dev, have to debug --- lib/main.dart | 192 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 175 insertions(+), 17 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c4dbbab..ec23f7d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,17 +15,42 @@ void main() async { } class MainApp extends StatelessWidget { - void writeData() async { - DatabaseReference ref = FirebaseDatabase.instance.ref("questions/2"); + Future getBlueThumb(id) { + DatabaseReference ref = + FirebaseDatabase.instance.ref("questions/$id/blue_thumb"); - await ref.set({ - "blue_thumb": 0, - "id": 2, - "red_thumb": 0, - "text": "Mon comptable rêve de bible et de gros chiffres." + return ref.once().then((event) { + return event.snapshot.value; + }); + } + + void increaseBlueThumb(id) async { + int blueThumb = await getBlueThumb(id) as int; + blueThumb++; + DatabaseReference ref = + FirebaseDatabase.instance.ref("questions/$id/blue_thumb"); + + await ref.set(blueThumb); + } + + Future getRedThumb(id) { + DatabaseReference ref = + FirebaseDatabase.instance.ref("questions/$id/red_thumb"); + + return ref.once().then((event) { + return event.snapshot.value; }); } + void increaseRedThumb(id) async { + int blueThumb = await getBlueThumb(id) as int; + blueThumb++; + DatabaseReference ref = + FirebaseDatabase.instance.ref("questions/$id/red_thumb"); + + await ref.set(blueThumb); + } + /// Get data from Firebase at questions/$id/text /// Return a Future Future getData(id) async { @@ -35,24 +60,157 @@ class MainApp extends StatelessWidget { return event.snapshot.value; } + QuestionList questionList = QuestionList(); + @override Widget build(BuildContext context) { - int id = Random().nextInt(2); return MaterialApp( home: Scaffold( body: Center( - child: FutureBuilder( - future: getData(id), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text(snapshot.data.toString()); - } else { - return const CircularProgressIndicator(); - } + child: Column(children: [ + ElevatedButton( + onPressed: () { + increaseBlueThumb(Random().nextInt(3)); }, + child: const Text("Increase blue thumb"), ), - ), + ElevatedButton( + onPressed: () { + increaseRedThumb(Random().nextInt(3)); + }, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.red)), + child: const Text("Increase red thumb")), + FutureBuilder( + future: questionList.getQuestion(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text(snapshot.data.toString()); + } else { + return const CircularProgressIndicator(); + } + }), + ])), ), ); } } + +class LocalQuestion { + int id = 0; + String text = ""; + int blueThumb = 0; + int redThumb = 0; + int usage = 0; + double repetitionScore = 0.0; + double globalScore = 0.0; + + LocalQuestion(this.id, this.text, this.blueThumb, this.redThumb) { + usage = 0; + repetitionScore = 1.0; + updateGlobalScore(); + } + + void increaseBlueThumb() { + blueThumb++; + } + + void increaseRedThumb() { + redThumb++; + } + + void increaseUsage() { + usage++; + } + + void addUsage(int elementNumber) { + usage++; + repetitionScore /= elementNumber; + updateGlobalScore(); + } + + void addNonUsage(int elementNumber) { + repetitionScore *= (1 + 1 / elementNumber); + updateGlobalScore(); + } + + void updateGlobalScore() { + globalScore = blueThumb / redThumb * repetitionScore; + } +} + +class QuestionList { + List questions = []; + + QuestionList() { + questions = []; + + Future>?> data = getData(); + + data.then((value) { + if (value != null) { + for (int i = 0; i < value.length; i++) { + int? id = value[i]["id"] as int?; + String? text = value[i]["text"] as String?; + int? blueThumb = value[i]["blue_thumb"] as int?; + int? redThumb = value[i]["red_thumb"] as int?; + + questions.add(LocalQuestion(id!, text!, blueThumb!, redThumb!)); + } + } + }); + } + + // Get data from Firebase at questions/ + // ignore: slash_for_doc_comments + /** Data format: + * [ + { + "blue_thumb": 89, + "id": 0, + "red_thumb": 90, + "text": "J'ai croisé un gars son front c'est un scenic" + }, + { + "blue_thumb": 48, + "id": 1, + "red_thumb": 49, + "text": "Pour ouvrir une porte, il ne faut pas mettre son ventre dans le pédiluve" + }, + { + "blue_thumb": 7, + "id": 2, + "red_thumb": 8, + "text": "Mon comptable rêve de bible et de gros chiffres." + } + ] + */ + Future>?> getData() async { + DatabaseReference ref = FirebaseDatabase.instance.ref("questions"); + + DatabaseEvent event = await ref.once(); + if (event.snapshot.value != null) { + parseData(data) { + List>? list = data as List>?; + return list; + } + + Future>?> data = + compute(parseData, event.snapshot.value); + return data; + } else { + return null; + } + } + + Future getQuestion() async { + List choices = []; + for (int i = 0; i < questions.length; i++) { + for (int j = 0; j < questions[i].globalScore * 100; j++) { + choices.add(questions[i]); + } + } + + return choices[Random().nextInt(choices.length)]; + } +} From a31a0cdc6f78fc8a757e5d48d359c6845a3257e3 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 4 Mar 2023 09:30:50 +0100 Subject: [PATCH 08/37] FrontUpgrade --- lib/history_page.dart | 49 ++++++++++++++++++++------------------- lib/main.dart | 7 ++---- lib/main_page.dart | 53 ++++++++++++++++++++++++++----------------- lib/textstyle.dart | 2 +- 4 files changed, 59 insertions(+), 52 deletions(-) diff --git a/lib/history_page.dart b/lib/history_page.dart index 5c2c465..5472cda 100644 --- a/lib/history_page.dart +++ b/lib/history_page.dart @@ -26,31 +26,31 @@ class _HistoryPageState extends State { return Column(children: [ const SizedBox(height: 100), Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 50), + const SizedBox(height: 25), Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 100), + const SizedBox(height: 50), const Flexible(child: History()), - //Text(userPost, style: titleText2), - Center( + Container( + alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 75, right: 75), - child: TextField( - controller: _textController, - style: const TextStyle(color: Colors.black, fontSize: 30), - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ), - )), + padding: const EdgeInsets.only(bottom: 75), + child: TextField( + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ), + )), ]); } } @@ -74,7 +74,6 @@ class _HistoryState extends State { return AnimatedList( key: _key, reverse: true, - padding: const EdgeInsets.only(top: 400), initialItemCount: appState.questionsList.length, itemBuilder: (context, index, animation) { final question = appState.questionsList[index]; @@ -84,10 +83,10 @@ class _HistoryState extends State { child: Column( children: [ const SizedBox(height: 20), - Text(question, style: titleText), + Text(question, style: titleText2), const SizedBox(height: 20), Text(reponseList[Random().nextInt(reponseList.length)], - style: titleText.copyWith(color: Colors.red)), + style: titleText2.copyWith(color: Colors.red)), ], ), ), diff --git a/lib/main.dart b/lib/main.dart index 4de9cc8..525f0d6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,7 +19,7 @@ class MainApp extends StatelessWidget { debugShowCheckedModeBanner: false, //debug banner desactivation title: 'Chat ChuisPt', theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.grey)), + colorScheme: ColorScheme.fromSeed(seedColor: Colors.black)), home: const MyHomePage(), ), ); @@ -102,10 +102,7 @@ class _MyHomePageState extends State { }); }, )), - Expanded( - child: Container( - //color: Theme.of(context).colorScheme.primaryContainer, - child: page)), + Expanded(child: Container(child: page)), ])); }); } diff --git a/lib/main_page.dart b/lib/main_page.dart index b3087aa..cda904d 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -26,16 +26,17 @@ class _MainPageState extends State { return Column(children: [ const SizedBox(height: 100), Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 50), + const SizedBox(height: 5), Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 100), - const Flexible(child: QuestionGrid()), + const SizedBox(height: 5), + Flexible(child: QuestionGrid()), Center( child: Padding( padding: const EdgeInsets.only(bottom: 75, right: 75), child: TextField( + textAlign: TextAlign.center, controller: _textController, - style: const TextStyle(color: Colors.black, fontSize: 30), + style: normalText, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: 'Posez votre question ici...', @@ -53,20 +54,33 @@ class _MainPageState extends State { } class QuestionGrid extends StatelessWidget { - const QuestionGrid({Key? key}) : super(key: key); + QuestionGrid({Key? key}) : super(key: key); + + List exemples = [ + "Qu'est-ce que le Covid-19 ?", + "Quels sont les symptômes du Covid-19 ?", + "Comment se transmet le Covid-19 ?", + "Quels sont les risques de contamination ?", + "Quel est la taille moyenne d'une famille ?", + "Quel est l'^age moyen d'un crabe ?", + "Quel est le nombre de personnes qui ont été contaminées ?", + "Pourquoi 42 ?", + "Quand est-ce que le monde va mourir ?", + ]; @override Widget build(BuildContext context) { List questions = []; - for (int i = 1; i <= 9; i++) { + for (int i = 1; i <= 8; i++) { questions.add( Container( - color: Colors.grey[300], - child: TextButton( - onPressed: onPressed, - child: Text('$i', style: titleText2), - ), - ), + color: Colors.grey[300], + child: TextButton( + onPressed: onPressed, + child: Text( + exemples[Random().nextInt(exemples.length)], + ), + )), ); } @@ -96,15 +110,12 @@ class QuestionGrid extends StatelessWidget { ], ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 150, right: 150), - child: GridView.count( - primary: false, - crossAxisSpacing: 75, - mainAxisSpacing: 30, - crossAxisCount: 3, - children: questions, - ), + child: GridView.count( + primary: false, + crossAxisSpacing: 15, + mainAxisSpacing: 15, + crossAxisCount: 2, + children: questions, ), ), ], diff --git a/lib/textstyle.dart b/lib/textstyle.dart index 264c546..19b6679 100644 --- a/lib/textstyle.dart +++ b/lib/textstyle.dart @@ -8,7 +8,7 @@ TextStyle normalText = const TextStyle( TextStyle titleText = const TextStyle( color: Colors.black, - fontSize: 60, + fontSize: 40, fontWeight: FontWeight.w700, ); From 531e1cb736ea65287d23f4cb9c60a63e228cc1a9 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 6 Mar 2023 12:03:18 +0100 Subject: [PATCH 09/37] main_page & hitsory merge --- lib/history_page.dart | 97 ----------------------------- lib/main.dart | 8 +-- lib/main_page.dart | 142 +++++++++++++++++++++++++++++++++--------- 3 files changed, 112 insertions(+), 135 deletions(-) delete mode 100644 lib/history_page.dart diff --git a/lib/history_page.dart b/lib/history_page.dart deleted file mode 100644 index 5472cda..0000000 --- a/lib/history_page.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'main.dart'; -import 'main_page.dart'; -import 'textstyle.dart'; - -class HistoryPage extends StatefulWidget { - const HistoryPage({Key? key}) : super(key: key); - - @override - State createState() => _HistoryPageState(); -} - -class _HistoryPageState extends State { - var selectedIndex = 1; - - // store the user's question - final _textController = TextEditingController(); - String userPost = ''; - - @override - Widget build(BuildContext context) { - return Column(children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 50), - const Flexible(child: History()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only(bottom: 75), - child: TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ), - )), - ]); - } -} - -class History extends StatefulWidget { - const History({Key? key}) : super(key: key); - - @override - State createState() => _HistoryState(); -} - -class _HistoryState extends State { - final _key = GlobalKey(); - List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; - - @override - Widget build(BuildContext context) { - final appState = context.watch(); - appState.historyKey = _key; - - return AnimatedList( - key: _key, - reverse: true, - initialItemCount: appState.questionsList.length, - itemBuilder: (context, index, animation) { - final question = appState.questionsList[index]; - return SizeTransition( - sizeFactor: animation, - child: Center( - child: Column( - children: [ - const SizedBox(height: 20), - Text(question, style: titleText2), - const SizedBox(height: 20), - Text(reponseList[Random().nextInt(reponseList.length)], - style: titleText2.copyWith(color: Colors.red)), - ], - ), - ), - ); - }, - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 525f0d6..d267730 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:chat_chuispt/history_page.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -39,11 +38,6 @@ class MainAppState extends ChangeNotifier { animatedList?.insertItem(0); } } - - void addResponse(String response) { - responsesList.add(response); - notifyListeners(); - } } class MyHomePage extends StatefulWidget { @@ -64,7 +58,7 @@ class _MyHomePageState extends State { page = MainPage(); break; case 1: - page = HistoryPage(); + page = const Text('Page2'); break; case 2: page = const Text('Page 3'); diff --git a/lib/main_page.dart b/lib/main_page.dart index cda904d..f74884a 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -23,46 +23,85 @@ class _MainPageState extends State { Widget build(BuildContext context) { var appState = context.watch(); - return Column(children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - Flexible(child: QuestionGrid()), - Center( - child: Padding( - padding: const EdgeInsets.only(bottom: 75, right: 75), - child: TextField( - textAlign: TextAlign.center, - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - //appState.Send(_textController); - _textController.clear(); - }, - icon: const Icon(Icons.send))), - ), - )), - Text(userPost, style: titleText2), - ]); + // ! LISTE VIDE ! // + if (appState.responsesList.isEmpty) { + return Column(children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + Flexible(child: QuestionGrid()), + const Flexible(child: History()), + Container( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 75), + child: TextField( + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ), + )), + ]); + } + + // ! LISTE NON VIDE ! // + else { + return Column(children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 50), + const Flexible(child: History()), + Container( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 75), + child: TextField( + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ), + )), + ]); + } } } class QuestionGrid extends StatelessWidget { QuestionGrid({Key? key}) : super(key: key); - List exemples = [ + final List exemples = [ "Qu'est-ce que le Covid-19 ?", "Quels sont les symptômes du Covid-19 ?", "Comment se transmet le Covid-19 ?", "Quels sont les risques de contamination ?", "Quel est la taille moyenne d'une famille ?", - "Quel est l'^age moyen d'un crabe ?", + "Quel est l'âge moyen d'un crabe ?", "Quel est le nombre de personnes qui ont été contaminées ?", "Pourquoi 42 ?", "Quand est-ce que le monde va mourir ?", @@ -112,8 +151,8 @@ class QuestionGrid extends StatelessWidget { Expanded( child: GridView.count( primary: false, - crossAxisSpacing: 15, - mainAxisSpacing: 15, + crossAxisSpacing: 5, + mainAxisSpacing: 5, crossAxisCount: 2, children: questions, ), @@ -124,3 +163,44 @@ class QuestionGrid extends StatelessWidget { void onPressed() {} } + +class History extends StatefulWidget { + const History({Key? key}) : super(key: key); + + @override + State createState() => _HistoryState(); +} + +class _HistoryState extends State { + final _key = GlobalKey(); + List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; + + @override + Widget build(BuildContext context) { + final appState = context.watch(); + appState.historyKey = _key; + + return AnimatedList( + key: _key, + reverse: true, + initialItemCount: appState.questionsList.length, + itemBuilder: (context, index, animation) { + final question = appState.questionsList[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( + child: Column( + children: [ + const SizedBox(height: 20), + Text(question, style: titleText2), + const SizedBox(height: 20), + Text(reponseList[Random().nextInt(reponseList.length)], + style: titleText2.copyWith(color: Colors.red)), + ], + ), + ), + ); + }, + ); + } +} From 3deec45b960896ea2f1398fcde1c966796ebd70d Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 6 Mar 2023 12:28:22 +0100 Subject: [PATCH 10/37] GraphicsUpgrade --- lib/main_page.dart | 12 ++++++++---- lib/textstyle.dart | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/main_page.dart b/lib/main_page.dart index f74884a..0dbfbe9 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -31,12 +31,12 @@ class _MainPageState extends State { const SizedBox(height: 5), Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), - Flexible(child: QuestionGrid()), - const Flexible(child: History()), + Expanded(child: QuestionGrid()), Container( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 75), + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), child: TextField( controller: _textController, style: normalText, @@ -69,7 +69,7 @@ class _MainPageState extends State { Container( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 75), + padding: const EdgeInsets.all(20), child: TextField( controller: _textController, style: normalText, @@ -110,6 +110,8 @@ class QuestionGrid extends StatelessWidget { @override Widget build(BuildContext context) { List questions = []; + + // * Génération du tableau aléatoire de questions * // for (int i = 1; i <= 8; i++) { questions.add( Container( @@ -118,11 +120,13 @@ class QuestionGrid extends StatelessWidget { onPressed: onPressed, child: Text( exemples[Random().nextInt(exemples.length)], + textAlign: TextAlign.center, ), )), ); } + // ! AFFICHAGE ! // return Column( children: [ Row( diff --git a/lib/textstyle.dart b/lib/textstyle.dart index 19b6679..36fe5da 100644 --- a/lib/textstyle.dart +++ b/lib/textstyle.dart @@ -17,3 +17,9 @@ TextStyle titleText2 = const TextStyle( fontSize: 20, fontWeight: FontWeight.w500, ); + + +// ! TODO ! // +// ? Régler le fait que la page princpale ne s'affiche pas --> setstate()? +// ? Rendre utilisable les propositions de questions à poser +// ? Créer manuellement le thème graphique de l'application From 2ab8ec25b2e5a94694dba538d86279a8ad0de580 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 6 Mar 2023 16:19:21 +0100 Subject: [PATCH 11/37] ColorScheme --- lib/main.dart | 21 +++++---- lib/main_page.dart | 104 +++++++++++++++++++++++++-------------------- lib/textstyle.dart | 21 +++++++-- 3 files changed, 87 insertions(+), 59 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index d267730..d9eac02 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:chat_chuispt/textstyle.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -17,8 +18,7 @@ class MainApp extends StatelessWidget { child: MaterialApp( debugShowCheckedModeBanner: false, //debug banner desactivation title: 'Chat ChuisPt', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.black)), + theme: themeApp, home: const MyHomePage(), ), ); @@ -73,18 +73,21 @@ class _MyHomePageState extends State { body: Row(children: [ SafeArea( child: NavigationRail( - destinations: const [ + destinations: [ NavigationRailDestination( - icon: Icon(Icons.home), - label: Text('Main'), + icon: Icon(Icons.home, + color: Theme.of(context).colorScheme.onPrimary), + label: const Text('Home'), ), NavigationRailDestination( - icon: Icon(Icons.favorite_border), - label: Text('Favorites'), + icon: Icon(Icons.favorite_border, + color: Theme.of(context).colorScheme.onPrimary), + label: const Text('Favorites'), ), NavigationRailDestination( - icon: Icon(Icons.bookmark_border), - label: Text('Bookmarks'), + icon: Icon(Icons.bookmark_border, + color: Theme.of(context).colorScheme.onPrimary), + label: const Text('Bookmarks'), ), ], diff --git a/lib/main_page.dart b/lib/main_page.dart index 0dbfbe9..f72801a 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -25,36 +25,45 @@ class _MainPageState extends State { // ! LISTE VIDE ! // if (appState.responsesList.isEmpty) { - return Column(children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - Expanded(child: QuestionGrid()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ), - )), - ]); + return Consumer(builder: (context, value, child) { + return Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + Expanded(child: QuestionGrid()), + Container( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: TextField( + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + suffixIcon: IconButton( + onPressed: () { + setState(() { + userPost = _textController.text; + context + .read() + .addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ), + )), + ], + ), + ); + }); } // ! LISTE NON VIDE ! // @@ -112,7 +121,7 @@ class QuestionGrid extends StatelessWidget { List questions = []; // * Génération du tableau aléatoire de questions * // - for (int i = 1; i <= 8; i++) { + for (int i = 1; i <= 4; i++) { questions.add( Container( color: Colors.grey[300], @@ -132,31 +141,32 @@ class QuestionGrid extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Center( - child: IconButton( - onPressed: onPressed, - icon: const Icon(Icons.favorite), - ), - ), - Center( - child: IconButton( - onPressed: onPressed, - icon: const Icon(Icons.home), + Padding( + padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), + child: Center( + child: Icon( + Icons.home, + color: themeApp.colorScheme.onPrimary, + ), ), ), - Center( - child: IconButton( - onPressed: onPressed, - icon: const Icon(Icons.work), + Padding( + padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), + child: Center( + child: Icon( + Icons.home, + color: themeApp.colorScheme.onPrimary, + ), ), ), ], ), Expanded( child: GridView.count( + padding: const EdgeInsets.only(right: 20, left: 20), primary: false, - crossAxisSpacing: 5, - mainAxisSpacing: 5, + crossAxisSpacing: 10, + mainAxisSpacing: 10, crossAxisCount: 2, children: questions, ), diff --git a/lib/textstyle.dart b/lib/textstyle.dart index 36fe5da..bdb26bb 100644 --- a/lib/textstyle.dart +++ b/lib/textstyle.dart @@ -1,23 +1,38 @@ import 'package:flutter/material.dart'; TextStyle normalText = const TextStyle( - color: Colors.black, + color: Colors.white, fontSize: 15, fontWeight: FontWeight.w300, ); TextStyle titleText = const TextStyle( - color: Colors.black, + color: Colors.white, fontSize: 40, fontWeight: FontWeight.w700, ); TextStyle titleText2 = const TextStyle( - color: Colors.black, + color: Colors.white, fontSize: 20, fontWeight: FontWeight.w500, ); +ThemeData themeApp = ThemeData( + colorScheme: ColorScheme( + primary: Colors.blueGrey.shade900, + secondary: Colors.blueGrey.shade500, + surface: Colors.blueGrey.shade900, + background: Colors.blueGrey.shade600, + error: Colors.red, + onPrimary: Colors.white, + onSecondary: Colors.white, + onSurface: Colors.blueGrey.shade400, + onBackground: Colors.blueGrey.shade400, + onError: Colors.black, + brightness: Brightness.light), +); + // ! TODO ! // // ? Régler le fait que la page princpale ne s'affiche pas --> setstate()? From 12afd0f9b1ff6a593eebde371df2f0772df2eb09 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 6 Mar 2023 18:05:10 +0100 Subject: [PATCH 12/37] HomePage and MainPage merged --- lib/main.dart | 2 + lib/main_page.dart | 151 ++++++++++++++++++++++++--------------------- 2 files changed, 81 insertions(+), 72 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index d9eac02..7c80594 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -73,6 +73,8 @@ class _MyHomePageState extends State { body: Row(children: [ SafeArea( child: NavigationRail( + extended: constraints.maxWidth >= 400, + backgroundColor: Theme.of(context).colorScheme.primary, destinations: [ NavigationRailDestination( icon: Icon(Icons.home, diff --git a/lib/main_page.dart b/lib/main_page.dart index f72801a..64eb516 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -22,82 +22,89 @@ class _MainPageState extends State { @override Widget build(BuildContext context) { var appState = context.watch(); + MyTextField textField = MyTextField(); // ! LISTE VIDE ! // - if (appState.responsesList.isEmpty) { - return Consumer(builder: (context, value, child) { - return Container( - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - Expanded(child: QuestionGrid()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - setState(() { - userPost = _textController.text; - context - .read() - .addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ), - )), - ], - ), - ); - }); - } - // ! LISTE NON VIDE ! // - else { - return Column(children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 50), - const Flexible(child: History()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(20), - child: TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - suffixIcon: IconButton( - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), + return Consumer(builder: (context, value, child) { + return appState.questionsList.isEmpty + ? Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + Expanded(child: QuestionGrid()), + Container( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField, + )), + ], ), - )), - ]); - } + ) + : + + // ! LISTE NON VIDE ! // + + Container( + color: themeApp.colorScheme.background, + child: Column(children: [ + const SizedBox(height: 100), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 50), + const Flexible(child: History()), + Container( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.all(20), child: textField)), + ]), + ); + }); + } +} + +class MyTextField extends StatefulWidget { + const MyTextField({Key? key}) : super(key: key); + + @override + State createState() => _MyTextFieldState(); +} + +class _MyTextFieldState extends State { + final _textController = TextEditingController(); + String userPost = ''; + + @override + Widget build(BuildContext context) { + return TextField( + autocorrect: true, + controller: _textController, + style: normalText, + decoration: InputDecoration( + filled: true, + fillColor: themeApp.colorScheme.primary, + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + labelStyle: normalText, + suffixIcon: IconButton( + color: themeApp.colorScheme.onPrimary, + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ); } } From 7d99230f39f2fc13aecca0c8017a2a09c6fb389e Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Thu, 9 Mar 2023 00:04:21 +0100 Subject: [PATCH 13/37] FrontUpgrades --- lib/main_page.dart | 66 +++++++++++++++++++++++++++++----------------- lib/textstyle.dart | 10 ++++++- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/main_page.dart b/lib/main_page.dart index 64eb516..b379ca0 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -38,13 +38,10 @@ class _MainPageState extends State { Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), Expanded(child: QuestionGrid()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField, - )), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), ], ), ) @@ -58,13 +55,8 @@ class _MainPageState extends State { const SizedBox(height: 100), Text('Chat ChuisPT', style: titleText), const SizedBox(height: 25), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 50), const Flexible(child: History()), - Container( - alignment: Alignment.bottomCenter, - child: Padding( - padding: const EdgeInsets.all(20), child: textField)), + Padding(padding: const EdgeInsets.all(20), child: textField), ]), ); }); @@ -89,8 +81,6 @@ class _MyTextFieldState extends State { controller: _textController, style: normalText, decoration: InputDecoration( - filled: true, - fillColor: themeApp.colorScheme.primary, border: const OutlineInputBorder(), labelText: 'Posez votre question ici...', labelStyle: normalText, @@ -108,9 +98,14 @@ class _MyTextFieldState extends State { } } -class QuestionGrid extends StatelessWidget { +class QuestionGrid extends StatefulWidget { QuestionGrid({Key? key}) : super(key: key); + @override + State createState() => _QuestionGridState(); +} + +class _QuestionGridState extends State { final List exemples = [ "Qu'est-ce que le Covid-19 ?", "Quels sont les symptômes du Covid-19 ?", @@ -126,14 +121,21 @@ class QuestionGrid extends StatelessWidget { @override Widget build(BuildContext context) { List questions = []; + MainAppState appState = context.watch(); // * Génération du tableau aléatoire de questions * // + String userPost = ""; for (int i = 1; i <= 4; i++) { questions.add( Container( color: Colors.grey[300], child: TextButton( - onPressed: onPressed, + onPressed: () { + setState(() { + userPost = exemples[Random().nextInt(exemples.length)]; + context.read().addQuestion(userPost); + }); + }, child: Text( exemples[Random().nextInt(exemples.length)], textAlign: TextAlign.center, @@ -152,7 +154,7 @@ class QuestionGrid extends StatelessWidget { padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), child: Center( child: Icon( - Icons.home, + Icons.help_center_outlined, color: themeApp.colorScheme.onPrimary, ), ), @@ -161,7 +163,7 @@ class QuestionGrid extends StatelessWidget { padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), child: Center( child: Icon( - Icons.home, + Icons.question_answer, color: themeApp.colorScheme.onPrimary, ), ), @@ -212,11 +214,27 @@ class _HistoryState extends State { child: Center( child: Column( children: [ - const SizedBox(height: 20), - Text(question, style: titleText2), - const SizedBox(height: 20), - Text(reponseList[Random().nextInt(reponseList.length)], - style: titleText2.copyWith(color: Colors.red)), + const SizedBox(height: 10), + Container( + // * QUESTION * // + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + )), + const SizedBox(height: 10), + Container( + // * REPONSES * // + color: themeApp.colorScheme.secondaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + reponseList[Random().nextInt(reponseList.length)], + style: titleText2, + textAlign: TextAlign.center, + ), + ), ], ), ), diff --git a/lib/textstyle.dart b/lib/textstyle.dart index bdb26bb..c5163de 100644 --- a/lib/textstyle.dart +++ b/lib/textstyle.dart @@ -20,11 +20,19 @@ TextStyle titleText2 = const TextStyle( ThemeData themeApp = ThemeData( colorScheme: ColorScheme( + + // ? PRIMAIRES ? // primary: Colors.blueGrey.shade900, secondary: Colors.blueGrey.shade500, + primaryContainer: Colors.blueGrey.shade700, + secondaryContainer: Colors.blueGrey.shade400, + error: Colors.red, + + // ? SURFACE ? // surface: Colors.blueGrey.shade900, background: Colors.blueGrey.shade600, - error: Colors.red, + + // ? ON ? // onPrimary: Colors.white, onSecondary: Colors.white, onSurface: Colors.blueGrey.shade400, From 6d56202825b51d04c5cbbbddfbcf555e42e17fec Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Mon, 13 Mar 2023 14:16:14 +0100 Subject: [PATCH 14/37] update gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 24476c5..5836224 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ migrate_working_dir/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line # is commented out by default. -#.vscode/ +.vscode/ # Flutter/Dart/Pub related **/doc/api/ From ef6245beceb0502cb26deb7d0e238504c65b0d14 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 14:51:00 +0100 Subject: [PATCH 15/37] ResetButton --- lib/main.dart | 76 +++------------------------- lib/main_page.dart | 120 +++++++++++++++++++++++++++++++-------------- lib/textstyle.dart | 1 + 3 files changed, 91 insertions(+), 106 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 7c80594..59023e4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,11 +16,10 @@ class MainApp extends StatelessWidget { return ChangeNotifierProvider( create: (context) => MainAppState(), child: MaterialApp( - debugShowCheckedModeBanner: false, //debug banner desactivation - title: 'Chat ChuisPt', - theme: themeApp, - home: const MyHomePage(), - ), + debugShowCheckedModeBanner: false, //debug banner desactivation + title: 'Chat ChuisPt', + theme: themeApp, + home: const MainPage()), ); } } @@ -38,71 +37,8 @@ class MainAppState extends ChangeNotifier { animatedList?.insertItem(0); } } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key}) : super(key: key); - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - var selectedIndex = 0; - - @override - Widget build(BuildContext context) { - Widget page; - switch (selectedIndex) { - case 0: - page = MainPage(); - break; - case 1: - page = const Text('Page2'); - break; - case 2: - page = const Text('Page 3'); - break; - - default: - throw UnimplementedError('no widget for $selectedIndex'); - } - - return LayoutBuilder(builder: (context, constraints) { - return Scaffold( - body: Row(children: [ - SafeArea( - child: NavigationRail( - extended: constraints.maxWidth >= 400, - backgroundColor: Theme.of(context).colorScheme.primary, - destinations: [ - NavigationRailDestination( - icon: Icon(Icons.home, - color: Theme.of(context).colorScheme.onPrimary), - label: const Text('Home'), - ), - NavigationRailDestination( - icon: Icon(Icons.favorite_border, - color: Theme.of(context).colorScheme.onPrimary), - label: const Text('Favorites'), - ), - NavigationRailDestination( - icon: Icon(Icons.bookmark_border, - color: Theme.of(context).colorScheme.onPrimary), - label: const Text('Bookmarks'), - ), - ], - // Destination selected - selectedIndex: selectedIndex, - onDestinationSelected: (value) { - setState(() { - selectedIndex = value; - }); - }, - )), - Expanded(child: Container(child: page)), - ])); - }); + void clearQuestionList() { + questionsList.clear(); } } diff --git a/lib/main_page.dart b/lib/main_page.dart index b379ca0..e2dd126 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -13,52 +13,103 @@ class MainPage extends StatefulWidget { } class _MainPageState extends State { - var selectedIndex = 0; - // store the user's question - final _textController = TextEditingController(); String userPost = ''; @override Widget build(BuildContext context) { var appState = context.watch(); - MyTextField textField = MyTextField(); + MyTextField textField = const MyTextField(); // ! LISTE VIDE ! // return Consumer(builder: (context, value, child) { - return appState.questionsList.isEmpty - ? Container( - color: themeApp.colorScheme.background, - child: Column( + return Scaffold( + body: appState.questionsList.isEmpty + ? Row( + children: [ + Container( + color: themeApp.colorScheme.primaryContainer, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context + .read() + .clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )), + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + Expanded(child: QuestionGrid()), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), + ], + ), + ), + ), + ], + ) + : + + // ! LISTE NON VIDE ! // + + Row( children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), + Container( + color: themeApp.colorScheme.primaryContainer, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context + .read() + .clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )), + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column(children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + const Flexible(child: History()), + Padding( + padding: const EdgeInsets.all(20), + child: textField), + ]), + ), + ), ], ), - ) - : - - // ! LISTE NON VIDE ! // - - Container( - color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 100), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding(padding: const EdgeInsets.all(20), child: textField), - ]), - ); + ); }); } } @@ -121,7 +172,6 @@ class _QuestionGridState extends State { @override Widget build(BuildContext context) { List questions = []; - MainAppState appState = context.watch(); // * Génération du tableau aléatoire de questions * // String userPost = ""; @@ -183,8 +233,6 @@ class _QuestionGridState extends State { ], ); } - - void onPressed() {} } class History extends StatefulWidget { diff --git a/lib/textstyle.dart b/lib/textstyle.dart index c5163de..6380efe 100644 --- a/lib/textstyle.dart +++ b/lib/textstyle.dart @@ -34,6 +34,7 @@ ThemeData themeApp = ThemeData( // ? ON ? // onPrimary: Colors.white, + onPrimaryContainer: Colors.white, onSecondary: Colors.white, onSurface: Colors.blueGrey.shade400, onBackground: Colors.blueGrey.shade400, From ff5a568a3ac99ba4ed3dd104e8fe84b2c3f2afbc Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 15:16:56 +0100 Subject: [PATCH 16/37] FrontUpgrades --- lib/main_page.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/main_page.dart b/lib/main_page.dart index e2dd126..ec16a9d 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -29,7 +29,7 @@ class _MainPageState extends State { ? Row( children: [ Container( - color: themeApp.colorScheme.primaryContainer, + color: themeApp.colorScheme.surface, width: 75, child: Column( children: [ @@ -57,7 +57,7 @@ class _MainPageState extends State { const SizedBox(height: 5), Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), - Expanded(child: QuestionGrid()), + const Expanded(child: QuestionGrid()), Padding( padding: const EdgeInsets.only( bottom: 25, top: 25, right: 15, left: 15), @@ -75,7 +75,7 @@ class _MainPageState extends State { Row( children: [ Container( - color: themeApp.colorScheme.primaryContainer, + color: themeApp.colorScheme.surface, width: 75, child: Column( children: [ @@ -150,7 +150,7 @@ class _MyTextFieldState extends State { } class QuestionGrid extends StatefulWidget { - QuestionGrid({Key? key}) : super(key: key); + const QuestionGrid({Key? key}) : super(key: key); @override State createState() => _QuestionGridState(); From 745f95319ea5fb4206c950d4325146d18a7d4803 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 15:31:28 +0100 Subject: [PATCH 17/37] Structure Upgrades --- lib/{textstyle.dart => constants.dart} | 6 -- lib/main.dart | 2 +- lib/main_page.dart | 87 ++++++++++++-------------- 3 files changed, 42 insertions(+), 53 deletions(-) rename lib/{textstyle.dart => constants.dart} (83%) diff --git a/lib/textstyle.dart b/lib/constants.dart similarity index 83% rename from lib/textstyle.dart rename to lib/constants.dart index 6380efe..e0d7ab4 100644 --- a/lib/textstyle.dart +++ b/lib/constants.dart @@ -41,9 +41,3 @@ ThemeData themeApp = ThemeData( onError: Colors.black, brightness: Brightness.light), ); - - -// ! TODO ! // -// ? Régler le fait que la page princpale ne s'affiche pas --> setstate()? -// ? Rendre utilisable les propositions de questions à poser -// ? Créer manuellement le thème graphique de l'application diff --git a/lib/main.dart b/lib/main.dart index 59023e4..fc66e02 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:chat_chuispt/textstyle.dart'; +import 'package:chat_chuispt/constants.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/main_page.dart b/lib/main_page.dart index ec16a9d..21e4314 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'textstyle.dart'; +import 'constants.dart'; import 'main.dart'; class MainPage extends StatefulWidget { @@ -20,33 +20,17 @@ class _MainPageState extends State { Widget build(BuildContext context) { var appState = context.watch(); MyTextField textField = const MyTextField(); - - // ! LISTE VIDE ! // + LeftBar leftBar = const LeftBar(); return Consumer(builder: (context, value, child) { return Scaffold( body: appState.questionsList.isEmpty - ? Row( + ? // ! SI LA LISTE EST VIDE ... + Row( children: [ - Container( - color: themeApp.colorScheme.surface, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context - .read() - .clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )), + // * LeftBar * // + leftBar, + // * Main * // Expanded( child: Container( color: themeApp.colorScheme.background, @@ -68,31 +52,12 @@ class _MainPageState extends State { ), ], ) - : - - // ! LISTE NON VIDE ! // - + : // ! SINON (liste non vide)... Row( children: [ - Container( - color: themeApp.colorScheme.surface, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context - .read() - .clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )), + // * LeftBar * // + leftBar, + // * Main * // Expanded( child: Container( color: themeApp.colorScheme.background, @@ -114,6 +79,36 @@ class _MainPageState extends State { } } +class LeftBar extends StatefulWidget { + const LeftBar({Key? key}) : super(key: key); + + @override + State createState() => _LeftBarState(); +} + +class _LeftBarState extends State { + @override + Widget build(BuildContext context) { + return Container( + color: themeApp.colorScheme.surface, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context.read().clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )); + } +} + class MyTextField extends StatefulWidget { const MyTextField({Key? key}) : super(key: key); From 29485bb0243d84be77d55fb288340035ffceb8a5 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 16:29:16 +0100 Subject: [PATCH 18/37] Structure Upgrade (Leftbar) --- lib/main.dart | 1 + lib/main_page.dart | 91 ++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fc66e02..9befdc8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,5 +40,6 @@ class MainAppState extends ChangeNotifier { void clearQuestionList() { questionsList.clear(); + notifyListeners(); } } diff --git a/lib/main_page.dart b/lib/main_page.dart index 21e4314..2014e95 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -18,62 +18,52 @@ class _MainPageState extends State { @override Widget build(BuildContext context) { - var appState = context.watch(); - MyTextField textField = const MyTextField(); - LeftBar leftBar = const LeftBar(); + // * Variables : + var appState = context.watch(); // état de l'application + MyTextField textField = const MyTextField(); // le champ de texte + LeftBar leftBar = const LeftBar(); // la barre de gauche return Consumer(builder: (context, value, child) { return Scaffold( - body: appState.questionsList.isEmpty - ? // ! SI LA LISTE EST VIDE ... - Row( - children: [ - // * LeftBar * // - leftBar, - // * Main * // - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - const Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), - ], - ), - ), + body: Row( + children: [ + leftBar, + if (appState.questionsList.isEmpty) + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + const Expanded(child: QuestionGrid()), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), + ], ), - ], + ), ) - : // ! SINON (liste non vide)... - Row( - children: [ - // * LeftBar * // - leftBar, - // * Main * // - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding( - padding: const EdgeInsets.all(20), - child: textField), - ]), - ), - ), - ], + else + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column(children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + const Flexible(child: History()), + Padding( + padding: const EdgeInsets.all(20), child: textField), + ]), + ), ), + ], + ), ); }); } @@ -123,7 +113,6 @@ class _MyTextFieldState extends State { @override Widget build(BuildContext context) { return TextField( - autocorrect: true, controller: _textController, style: normalText, decoration: InputDecoration( From cf1ae9512d30824cec1cf67aff1718ea963d95ca Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 16:44:36 +0100 Subject: [PATCH 19/37] files restructured --- lib/main page/components/History.dart | 63 +++++ lib/main page/components/QuestionsGrid.dart | 92 +++++++ lib/main page/components/TextField.dart | 39 +++ lib/main page/main_page.dart | 103 ++++++++ lib/main.dart | 2 +- lib/main_page.dart | 277 -------------------- 6 files changed, 298 insertions(+), 278 deletions(-) create mode 100644 lib/main page/components/History.dart create mode 100644 lib/main page/components/QuestionsGrid.dart create mode 100644 lib/main page/components/TextField.dart create mode 100644 lib/main page/main_page.dart delete mode 100644 lib/main_page.dart diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart new file mode 100644 index 0000000..d7561d2 --- /dev/null +++ b/lib/main page/components/History.dart @@ -0,0 +1,63 @@ +import '../../main.dart'; +import '../../constants.dart'; + +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class History extends StatefulWidget { + const History({Key? key}) : super(key: key); + + @override + State createState() => _HistoryState(); +} + +class _HistoryState extends State { + final _key = GlobalKey(); + List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; + + @override + Widget build(BuildContext context) { + final appState = context.watch(); + appState.historyKey = _key; + + return AnimatedList( + key: _key, + reverse: true, + initialItemCount: appState.questionsList.length, + itemBuilder: (context, index, animation) { + final question = appState.questionsList[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( + child: Column( + children: [ + const SizedBox(height: 10), + Container( + // * QUESTION * // + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + )), + const SizedBox(height: 10), + Container( + // * REPONSES * // + color: themeApp.colorScheme.secondaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + reponseList[Random().nextInt(reponseList.length)], + style: titleText2, + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/main page/components/QuestionsGrid.dart b/lib/main page/components/QuestionsGrid.dart new file mode 100644 index 0000000..d8dc1bf --- /dev/null +++ b/lib/main page/components/QuestionsGrid.dart @@ -0,0 +1,92 @@ +import '../../main.dart'; +import '../../constants.dart'; + +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class QuestionGrid extends StatefulWidget { + const QuestionGrid({Key? key}) : super(key: key); + + @override + State createState() => _QuestionGridState(); +} + +class _QuestionGridState extends State { + final List exemples = [ + "Qu'est-ce que le Covid-19 ?", + "Quels sont les symptômes du Covid-19 ?", + "Comment se transmet le Covid-19 ?", + "Quels sont les risques de contamination ?", + "Quel est la taille moyenne d'une famille ?", + "Quel est l'âge moyen d'un crabe ?", + "Quel est le nombre de personnes qui ont été contaminées ?", + "Pourquoi 42 ?", + "Quand est-ce que le monde va mourir ?", + ]; + + @override + Widget build(BuildContext context) { + List questions = []; + + // * Génération du tableau aléatoire de questions * // + String userPost = ""; + for (int i = 1; i <= 4; i++) { + questions.add( + Container( + color: Colors.grey[300], + child: TextButton( + onPressed: () { + setState(() { + userPost = exemples[Random().nextInt(exemples.length)]; + context.read().addQuestion(userPost); + }); + }, + child: Text( + exemples[Random().nextInt(exemples.length)], + textAlign: TextAlign.center, + ), + )), + ); + } + + // ! AFFICHAGE ! // + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), + child: Center( + child: Icon( + Icons.help_center_outlined, + color: themeApp.colorScheme.onPrimary, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), + child: Center( + child: Icon( + Icons.question_answer, + color: themeApp.colorScheme.onPrimary, + ), + ), + ), + ], + ), + Expanded( + child: GridView.count( + padding: const EdgeInsets.only(right: 20, left: 20), + primary: false, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + crossAxisCount: 2, + children: questions, + ), + ), + ], + ); + } +} diff --git a/lib/main page/components/TextField.dart b/lib/main page/components/TextField.dart new file mode 100644 index 0000000..f1cbff1 --- /dev/null +++ b/lib/main page/components/TextField.dart @@ -0,0 +1,39 @@ +import '../../main.dart'; +import '../../constants.dart'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class MyTextField extends StatefulWidget { + const MyTextField({Key? key}) : super(key: key); + + @override + State createState() => _MyTextFieldState(); +} + +class _MyTextFieldState extends State { + final _textController = TextEditingController(); + String userPost = ''; + + @override + Widget build(BuildContext context) { + return TextField( + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + labelStyle: normalText, + suffixIcon: IconButton( + color: themeApp.colorScheme.onPrimary, + onPressed: () { + setState(() { + userPost = _textController.text; + context.read().addQuestion(userPost); + _textController.clear(); + }); + }, + icon: const Icon(Icons.send))), + ); + } +} diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart new file mode 100644 index 0000000..aa4afd3 --- /dev/null +++ b/lib/main page/main_page.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../constants.dart'; +import '../main.dart'; + +import 'components/History.dart'; +import 'components/QuestionsGrid.dart'; +import 'components/TextField.dart'; + +class MainPage extends StatefulWidget { + const MainPage({Key? key}) : super(key: key); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + // store the user's question + String userPost = ''; + + @override + Widget build(BuildContext context) { + // * Variables : + var appState = context.watch(); // état de l'application + MyTextField textField = const MyTextField(); // le champ de texte + LeftBar leftBar = const LeftBar(); // la barre de gauche + + return Consumer(builder: (context, value, child) { + return Scaffold( + body: Row( + children: [ + leftBar, + if (appState.questionsList.isEmpty) + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + const Expanded(child: QuestionGrid()), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), + ], + ), + ), + ) + else + Expanded( + child: Container( + color: themeApp.colorScheme.background, + child: Column(children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + const Flexible(child: History()), + Padding( + padding: const EdgeInsets.all(20), child: textField), + ]), + ), + ), + ], + ), + ); + }); + } +} + +class LeftBar extends StatefulWidget { + const LeftBar({Key? key}) : super(key: key); + + @override + State createState() => _LeftBarState(); +} + +class _LeftBarState extends State { + @override + Widget build(BuildContext context) { + return Container( + color: themeApp.colorScheme.surface, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context.read().clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )); + } +} diff --git a/lib/main.dart b/lib/main.dart index 9befdc8..63c48c0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,7 @@ import 'package:chat_chuispt/constants.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'main_page.dart'; +import 'main page/main_page.dart'; void main() { runApp(const MainApp()); diff --git a/lib/main_page.dart b/lib/main_page.dart deleted file mode 100644 index 2014e95..0000000 --- a/lib/main_page.dart +++ /dev/null @@ -1,277 +0,0 @@ -import 'dart:math'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'constants.dart'; -import 'main.dart'; - -class MainPage extends StatefulWidget { - const MainPage({Key? key}) : super(key: key); - - @override - State createState() => _MainPageState(); -} - -class _MainPageState extends State { - // store the user's question - String userPost = ''; - - @override - Widget build(BuildContext context) { - // * Variables : - var appState = context.watch(); // état de l'application - MyTextField textField = const MyTextField(); // le champ de texte - LeftBar leftBar = const LeftBar(); // la barre de gauche - - return Consumer(builder: (context, value, child) { - return Scaffold( - body: Row( - children: [ - leftBar, - if (appState.questionsList.isEmpty) - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - const Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), - ], - ), - ), - ) - else - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding( - padding: const EdgeInsets.all(20), child: textField), - ]), - ), - ), - ], - ), - ); - }); - } -} - -class LeftBar extends StatefulWidget { - const LeftBar({Key? key}) : super(key: key); - - @override - State createState() => _LeftBarState(); -} - -class _LeftBarState extends State { - @override - Widget build(BuildContext context) { - return Container( - color: themeApp.colorScheme.surface, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context.read().clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )); - } -} - -class MyTextField extends StatefulWidget { - const MyTextField({Key? key}) : super(key: key); - - @override - State createState() => _MyTextFieldState(); -} - -class _MyTextFieldState extends State { - final _textController = TextEditingController(); - String userPost = ''; - - @override - Widget build(BuildContext context) { - return TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - labelStyle: normalText, - suffixIcon: IconButton( - color: themeApp.colorScheme.onPrimary, - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ); - } -} - -class QuestionGrid extends StatefulWidget { - const QuestionGrid({Key? key}) : super(key: key); - - @override - State createState() => _QuestionGridState(); -} - -class _QuestionGridState extends State { - final List exemples = [ - "Qu'est-ce que le Covid-19 ?", - "Quels sont les symptômes du Covid-19 ?", - "Comment se transmet le Covid-19 ?", - "Quels sont les risques de contamination ?", - "Quel est la taille moyenne d'une famille ?", - "Quel est l'âge moyen d'un crabe ?", - "Quel est le nombre de personnes qui ont été contaminées ?", - "Pourquoi 42 ?", - "Quand est-ce que le monde va mourir ?", - ]; - - @override - Widget build(BuildContext context) { - List questions = []; - - // * Génération du tableau aléatoire de questions * // - String userPost = ""; - for (int i = 1; i <= 4; i++) { - questions.add( - Container( - color: Colors.grey[300], - child: TextButton( - onPressed: () { - setState(() { - userPost = exemples[Random().nextInt(exemples.length)]; - context.read().addQuestion(userPost); - }); - }, - child: Text( - exemples[Random().nextInt(exemples.length)], - textAlign: TextAlign.center, - ), - )), - ); - } - - // ! AFFICHAGE ! // - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Padding( - padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.help_center_outlined, - color: themeApp.colorScheme.onPrimary, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.question_answer, - color: themeApp.colorScheme.onPrimary, - ), - ), - ), - ], - ), - Expanded( - child: GridView.count( - padding: const EdgeInsets.only(right: 20, left: 20), - primary: false, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - crossAxisCount: 2, - children: questions, - ), - ), - ], - ); - } -} - -class History extends StatefulWidget { - const History({Key? key}) : super(key: key); - - @override - State createState() => _HistoryState(); -} - -class _HistoryState extends State { - final _key = GlobalKey(); - List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; - - @override - Widget build(BuildContext context) { - final appState = context.watch(); - appState.historyKey = _key; - - return AnimatedList( - key: _key, - reverse: true, - initialItemCount: appState.questionsList.length, - itemBuilder: (context, index, animation) { - final question = appState.questionsList[index]; - return SizeTransition( - sizeFactor: animation, - child: Center( - child: Column( - children: [ - const SizedBox(height: 10), - Container( - // * QUESTION * // - color: themeApp.colorScheme.primaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - question, - style: titleText2, - textAlign: TextAlign.center, - )), - const SizedBox(height: 10), - Container( - // * REPONSES * // - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: titleText2, - textAlign: TextAlign.center, - ), - ), - ], - ), - ), - ); - }, - ); - } -} From abad20df8f4d183b87d3cc32d827747b6d1c094f Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 13 Mar 2023 16:55:52 +0100 Subject: [PATCH 20/37] AnimatedList -> ListView --- lib/main page/components/History.dart | 61 +++++++++++++-------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index d7561d2..85834ec 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -21,40 +21,39 @@ class _HistoryState extends State { final appState = context.watch(); appState.historyKey = _key; - return AnimatedList( - key: _key, + return ListView.builder( reverse: true, - initialItemCount: appState.questionsList.length, - itemBuilder: (context, index, animation) { + itemCount: appState.questionsList.length, + itemBuilder: (context, index) { final question = appState.questionsList[index]; - return SizeTransition( - sizeFactor: animation, - child: Center( - child: Column( - children: [ - const SizedBox(height: 10), - Container( - // * QUESTION * // - color: themeApp.colorScheme.primaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - question, - style: titleText2, - textAlign: TextAlign.center, - )), - const SizedBox(height: 10), - Container( - // * REPONSES * // - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: titleText2, - textAlign: TextAlign.center, - ), + return Center( + child: Column( + children: [ + const SizedBox(height: 10), + + // * QUESTION * // + Container( + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 10), + + // * REPONSES * // + Container( + color: themeApp.colorScheme.secondaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + reponseList[Random().nextInt(reponseList.length)], + style: titleText2, + textAlign: TextAlign.center, ), - ], - ), + ), + ], ), ); }, From 8a3f582e888e57abbddcce07f1fc64e91985c074 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Thu, 23 Mar 2023 19:59:54 +0100 Subject: [PATCH 21/37] A commit just for be able to get the code from another computer. There some many news on the questions, but nothing is currently working. I'm trying to show the content of database, I can see it on the debug mode, watching the value of data, but nothing is arriving at the screen --- lib/main.dart | 208 ++-------------------------------------- lib/question.dart | 189 ++++++++++++++++++++++++++++++++++++ lib/question2.dart | 52 ++++++++++ pubspec.lock | 176 ++++++++++++++++++++++++++++++++++ pubspec.yaml | 2 + test/question_test.dart | 31 ++++++ 6 files changed, 459 insertions(+), 199 deletions(-) create mode 100644 lib/question.dart create mode 100644 lib/question2.dart create mode 100644 test/question_test.dart diff --git a/lib/main.dart b/lib/main.dart index ec23f7d..722b669 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,216 +1,26 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:chat_chuispt/question.dart'; +import 'package:chat_chuispt/question2.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; + import 'firebase_options.dart'; -import 'package:firebase_database/firebase_database.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - runApp(MainApp()); } class MainApp extends StatelessWidget { - Future getBlueThumb(id) { - DatabaseReference ref = - FirebaseDatabase.instance.ref("questions/$id/blue_thumb"); - - return ref.once().then((event) { - return event.snapshot.value; - }); - } - - void increaseBlueThumb(id) async { - int blueThumb = await getBlueThumb(id) as int; - blueThumb++; - DatabaseReference ref = - FirebaseDatabase.instance.ref("questions/$id/blue_thumb"); - - await ref.set(blueThumb); - } - - Future getRedThumb(id) { - DatabaseReference ref = - FirebaseDatabase.instance.ref("questions/$id/red_thumb"); - - return ref.once().then((event) { - return event.snapshot.value; - }); - } + MainApp({super.key}); - void increaseRedThumb(id) async { - int blueThumb = await getBlueThumb(id) as int; - blueThumb++; - DatabaseReference ref = - FirebaseDatabase.instance.ref("questions/$id/red_thumb"); - - await ref.set(blueThumb); - } - - /// Get data from Firebase at questions/$id/text - /// Return a Future - Future getData(id) async { - DatabaseReference ref = FirebaseDatabase.instance.ref("questions/$id/text"); - - DatabaseEvent event = await ref.once(); - return event.snapshot.value; - } - - QuestionList questionList = QuestionList(); + final stream = DatabaseService().getTexts(); @override Widget build(BuildContext context) { return MaterialApp( - home: Scaffold( - body: Center( - child: Column(children: [ - ElevatedButton( - onPressed: () { - increaseBlueThumb(Random().nextInt(3)); - }, - child: const Text("Increase blue thumb"), - ), - ElevatedButton( - onPressed: () { - increaseRedThumb(Random().nextInt(3)); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.red)), - child: const Text("Increase red thumb")), - FutureBuilder( - future: questionList.getQuestion(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text(snapshot.data.toString()); - } else { - return const CircularProgressIndicator(); - } - }), - ])), - ), - ); - } -} - -class LocalQuestion { - int id = 0; - String text = ""; - int blueThumb = 0; - int redThumb = 0; - int usage = 0; - double repetitionScore = 0.0; - double globalScore = 0.0; - - LocalQuestion(this.id, this.text, this.blueThumb, this.redThumb) { - usage = 0; - repetitionScore = 1.0; - updateGlobalScore(); - } - - void increaseBlueThumb() { - blueThumb++; - } - - void increaseRedThumb() { - redThumb++; - } - - void increaseUsage() { - usage++; - } - - void addUsage(int elementNumber) { - usage++; - repetitionScore /= elementNumber; - updateGlobalScore(); - } - - void addNonUsage(int elementNumber) { - repetitionScore *= (1 + 1 / elementNumber); - updateGlobalScore(); - } - - void updateGlobalScore() { - globalScore = blueThumb / redThumb * repetitionScore; - } -} - -class QuestionList { - List questions = []; - - QuestionList() { - questions = []; - - Future>?> data = getData(); - - data.then((value) { - if (value != null) { - for (int i = 0; i < value.length; i++) { - int? id = value[i]["id"] as int?; - String? text = value[i]["text"] as String?; - int? blueThumb = value[i]["blue_thumb"] as int?; - int? redThumb = value[i]["red_thumb"] as int?; - - questions.add(LocalQuestion(id!, text!, blueThumb!, redThumb!)); - } - } - }); - } - - // Get data from Firebase at questions/ - // ignore: slash_for_doc_comments - /** Data format: - * [ - { - "blue_thumb": 89, - "id": 0, - "red_thumb": 90, - "text": "J'ai croisé un gars son front c'est un scenic" - }, - { - "blue_thumb": 48, - "id": 1, - "red_thumb": 49, - "text": "Pour ouvrir une porte, il ne faut pas mettre son ventre dans le pédiluve" - }, - { - "blue_thumb": 7, - "id": 2, - "red_thumb": 8, - "text": "Mon comptable rêve de bible et de gros chiffres." - } - ] - */ - Future>?> getData() async { - DatabaseReference ref = FirebaseDatabase.instance.ref("questions"); - - DatabaseEvent event = await ref.once(); - if (event.snapshot.value != null) { - parseData(data) { - List>? list = data as List>?; - return list; - } - - Future>?> data = - compute(parseData, event.snapshot.value); - return data; - } else { - return null; - } - } - - Future getQuestion() async { - List choices = []; - for (int i = 0; i < questions.length; i++) { - for (int j = 0; j < questions[i].globalScore * 100; j++) { - choices.add(questions[i]); - } - } - - return choices[Random().nextInt(choices.length)]; + home: Scaffold( + body: Center(child: TextListWidget(stream: stream)), + )); } } diff --git a/lib/question.dart b/lib/question.dart new file mode 100644 index 0000000..a55c800 --- /dev/null +++ b/lib/question.dart @@ -0,0 +1,189 @@ +import 'dart:math'; + +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/foundation.dart'; + +class LocalQuestion { + int id = 0; + String text = ""; + int blueThumb = 0; + int redThumb = 0; + int usage = 0; + double repetitionScore = 1.0; + double globalScore = 0.0; + + LocalQuestion( + {required this.id, + required this.text, + required this.blueThumb, + required this.redThumb}) { + updateGlobalScore(); + } + + String getText() { + return text; + } + + void increaseBlueThumb() { + blueThumb++; + } + + void increaseRedThumb() { + redThumb++; + } + + void increaseUsage() { + usage++; + } + + void addUsage(int elementNumber) { + usage++; + repetitionScore /= elementNumber; + updateGlobalScore(); + } + + void addNonUsage(int elementNumber) { + repetitionScore *= (1 + 1 / elementNumber); + updateGlobalScore(); + } + + void updateGlobalScore() { + globalScore = blueThumb / redThumb * repetitionScore; + } + + static LocalQuestion fromJson(Map? json) { + return LocalQuestion( + id: json?['id'] ?? 0, + text: json?['text'] ?? "", + blueThumb: json?['blue_thumb'] ?? 0, + redThumb: json?['red_thumb'] ?? 0, + ); + } +} + +class QuestionList { + late List questions; + + QuestionList() { + questions = []; + loadQuestions(); + } + + Future loadQuestions() async { + final data = await _getData(); + + if (data != null) { + questions = + data.map((question) => LocalQuestion.fromJson(question)).toList(); + } + } + + Future>?> _getData() async { + try { + final database = FirebaseDatabase.instance; + final ref = database.ref("questions"); + + final snapshot = await ref.once(); + final value = snapshot.snapshot.value; + + if (value is List) { + final data = value + .map((item) => { + "id": item["id"], + "text": item["text"], + "blue_thumb": item["blue_thumb"], + "red_thumb": item["red_thumb"], + }) + .toList(); + + return data; + } else { + if (kDebugMode) { + print("Invalid data format"); + } + return null; + } + } catch (error) { + if (kDebugMode) { + print("Error loading questions: $error"); + } + return null; + } + } + + Future getQuestion() async { + final choices = []; + + for (final question in questions) { + final score = question.globalScore * 100; + + for (var i = 0; i < score; i++) { + choices.add(question); + } + } + + return choices.isEmpty ? null : choices[Random().nextInt(choices.length)]; + } + + Future getBlueThumb(int id) async { + try { + final database = FirebaseDatabase.instance; + final ref = database.ref().child("questions/$id/blue_thumb"); + + final snapshot = await ref.once(); + final value = snapshot.snapshot.value; + + return value as int?; + } catch (error) { + if (kDebugMode) { + print("Error getting blue thumb count: $error"); + } + return null; + } + } + + Future increaseBlueThumb(int id) async { + try { + final database = FirebaseDatabase.instance; + final ref = database.ref().child("questions/$id/blue_thumb"); + + final currentCount = await getBlueThumb(id) ?? 0; + await ref.set(currentCount + 1); + } catch (error) { + if (kDebugMode) { + print("Error increasing blue thumb count: $error"); + } + } + } + + Future getRedThumb(int id) async { + try { + final database = FirebaseDatabase.instance; + final ref = database.ref().child("questions/$id/red_thumb"); + + final snapshot = await ref.once(); + final value = snapshot.snapshot.value; + + return value as int?; + } catch (error) { + if (kDebugMode) { + print("Error getting red thumb count: $error"); + } + return null; + } + } + + Future increaseRedThumb(int id) async { + try { + final database = FirebaseDatabase.instance; + final ref = database.ref().child("questions/$id/red_thumb"); + + final currentCount = await getRedThumb(id) ?? 0; + await ref.set(currentCount + 1); + } catch (error) { + if (kDebugMode) { + print("Error increasing red thumb count: $error"); + } + } + } +} diff --git a/lib/question2.dart b/lib/question2.dart new file mode 100644 index 0000000..78070e4 --- /dev/null +++ b/lib/question2.dart @@ -0,0 +1,52 @@ +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/material.dart'; + +class DatabaseService { + final DatabaseReference _database = FirebaseDatabase.instance.ref(''); + + Stream> getTexts() { + return _database.child('questions').onValue.map((event) { + final texts = []; + final Map? data = event.snapshot.value as Map?; + if (data != null) { + data.forEach((dynamic key, dynamic value) { + final text = value['text'] as String?; + if (text != null) { + texts.add(text); + } + }); + } + + return texts; + }); + } +} + +class TextListWidget extends StatelessWidget { + final Stream> stream; + + const TextListWidget({Key? key, required this.stream}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StreamBuilder>( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + final texts = snapshot.data!; + return ListView.builder( + itemCount: texts.length, + itemBuilder: (context, index) { + final text = texts[index]; + return ListTile( + title: Text(text), + ); + }, + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 7e20d8d..0ce6e57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "98d1d33ed129b372846e862de23a0fc365745f4d7b5e786ce667fcbbb7ac5c07" + url: "https://pub.dev" + source: hosted + version: "55.0.0" _flutterfire_internals: dependency: transitive description: @@ -9,6 +17,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.16" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "881348aed9b0b425882c97732629a6a31093c8ff20fc4b3b03fb9d3d50a3a126" + url: "https://pub.dev" + source: hosted + version: "5.7.1" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: @@ -25,6 +49,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + url: "https://pub.dev" + source: hosted + version: "8.4.4" characters: dependency: transitive description: @@ -41,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" collection: dependency: transitive description: @@ -49,6 +105,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "5be16bf1707658e4c03078d4a9b90208ded217fb02c163e207d334082412f2fb" + url: "https://pub.dev" + source: hosted + version: "2.2.5" fake_async: dependency: transitive description: @@ -57,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" firebase_core: dependency: "direct main" description: @@ -89,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.14" + firebase_database_mocks: + dependency: "direct main" + description: + name: firebase_database_mocks + sha256: cc3b48c897fd001b5d3d27564ba2994c7d9206e2235eb5792a385d5272f16e42 + url: "https://pub.dev" + source: hosted + version: "0.6.0" firebase_database_platform_interface: dependency: transitive description: @@ -105,6 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1+23" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -128,6 +232,14 @@ packages: description: flutter source: sdk version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" js: dependency: transitive description: @@ -144,6 +256,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" + source: hosted + version: "1.1.1" matcher: dependency: transitive description: @@ -168,6 +288,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + mockito: + dependency: "direct main" + description: + name: mockito + sha256: "2a8a17b82b1bde04d514e75d90d634a0ac23f6cb4991f6098009dd56836aeafe" + url: "https://pub.dev" + source: hosted + version: "5.3.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -184,11 +320,27 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298 + url: "https://pub.dev" + source: hosted + version: "1.2.7" source_span: dependency: transitive description: @@ -237,6 +389,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.18" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" vector_math: dependency: transitive description: @@ -245,6 +405,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=2.19.0 <4.0.0" flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9522e9b..cc3dff5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,8 @@ dependencies: firebase_database: ^10.0.14 flutter: sdk: flutter + mockito: ^5.3.2 + firebase_database_mocks: ^0.6.0 dev_dependencies: flutter_test: diff --git a/test/question_test.dart b/test/question_test.dart new file mode 100644 index 0000000..dc9e760 --- /dev/null +++ b/test/question_test.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:chat_chuispt/question.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class DataSnapshotMock extends Mock implements DataSnapshot { + DataSnapshotMock(this.value); + + @override + final dynamic value; +} + +class MockFirebaseDatabase extends Mock implements FirebaseDatabase {} + +void main() { + LocalQuestion localQuestionTest = LocalQuestion( + id: 1, + text: 'test', + blueThumb: 1, + redThumb: 1, + ); + + test('localQuestion Constructor', () { + expect(localQuestionTest.id, 1); + expect(localQuestionTest.text, 'test'); + expect(localQuestionTest.blueThumb, 1); + expect(localQuestionTest.redThumb, 1); + }); +} From 5ca7e752a2a536617756180bee2a747bdbb16c63 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Thu, 23 Mar 2023 21:42:49 +0100 Subject: [PATCH 22/37] The DatabaseService class is working! Now we can get the list of questions with the getTexts() function, wich is returning a Stream of list of Strings --- lib/main.dart | 10 +++++++--- lib/question2.dart | 9 +++++---- pubspec.lock | 26 +++++++++++++------------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 722b669..a73134b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:chat_chuispt/question.dart'; import 'package:chat_chuispt/question2.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; @@ -7,8 +6,13 @@ import 'firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - runApp(MainApp()); + try { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform); + runApp(MainApp()); + } catch (e) { + print(e); + } } class MainApp extends StatelessWidget { diff --git a/lib/question2.dart b/lib/question2.dart index 78070e4..48fee89 100644 --- a/lib/question2.dart +++ b/lib/question2.dart @@ -7,14 +7,15 @@ class DatabaseService { Stream> getTexts() { return _database.child('questions').onValue.map((event) { final texts = []; - final Map? data = event.snapshot.value as Map?; + + final List? data = event.snapshot.value as List?; if (data != null) { - data.forEach((dynamic key, dynamic value) { - final text = value['text'] as String?; + for (final question in data) { + final text = question['text'] as String?; if (text != null) { texts.add(text); } - }); + } } return texts; diff --git a/pubspec.lock b/pubspec.lock index 0ce6e57..46411b3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.0" convert: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.6.5" lints: dependency: transitive description: @@ -268,10 +268,10 @@ packages: dependency: transitive description: name: matcher - sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8 + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" url: "https://pub.dev" source: hosted - version: "0.12.14" + version: "0.12.13" material_color_utilities: dependency: transitive description: @@ -284,10 +284,10 @@ packages: dependency: transitive description: name: meta - sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.0" mockito: dependency: "direct main" description: @@ -308,10 +308,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.8.2" plugin_platform_interface: dependency: transitive description: @@ -385,10 +385,10 @@ packages: dependency: transitive description: name: test_api - sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 url: "https://pub.dev" source: hosted - version: "0.4.18" + version: "0.4.16" typed_data: dependency: transitive description: @@ -422,5 +422,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <4.0.0" + dart: ">=2.19.0 <3.0.0" flutter: ">=1.20.0" From 098346c94a438a26635120d3461b06fda2199dea Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Fri, 24 Mar 2023 00:02:21 +0100 Subject: [PATCH 23/37] Add question and delete question features are working, and a few repo refinement --- lib/database_service.dart | 85 ++++++++++++++++++++++ lib/main.dart | 28 +++++--- lib/question.dart | 132 ---------------------------------- lib/question2.dart | 53 -------------- lib/show_features_widget.dart | 124 ++++++++++++++++++++++++++++++++ test/database_test.dart | 0 test/question_test.dart | 30 -------- 7 files changed, 229 insertions(+), 223 deletions(-) create mode 100644 lib/database_service.dart delete mode 100644 lib/question2.dart create mode 100644 lib/show_features_widget.dart create mode 100644 test/database_test.dart diff --git a/lib/database_service.dart b/lib/database_service.dart new file mode 100644 index 0000000..643d8fe --- /dev/null +++ b/lib/database_service.dart @@ -0,0 +1,85 @@ +import 'package:firebase_database/firebase_database.dart'; + +class DatabaseService { + final DatabaseReference _database = FirebaseDatabase.instance.ref(''); + + // get all the texts from the database + Stream> getTexts() { + return _database.child('questions').onValue.map((event) { + final texts = []; + + final List? data = event.snapshot.value as List?; + if (data != null) { + for (final question in data) { + final text = question['text'] as String?; + if (text != null) { + texts.add(text); + } + } + } + + return texts; + }); + } + + // data format : {questions: {id: {text: text, blue_thumb: blueThumb, red_thumb: redThumb}}, ...} + // get all the data from the database + Stream>> getData() { + return _database.child('questions').onValue.map((event) { + final data = >[]; + + final Map? questions = + event.snapshot.value as Map?; + if (questions != null) { + questions.forEach((key, value) { + final Map? questionMap = + value as Map?; + + final text = questionMap?['text'] as String?; + final blueThumb = questionMap?['blue_thumb'] as int?; + final redThumb = questionMap?['red_thumb'] as int?; + + if (text != null && blueThumb != null && redThumb != null) { + data.add({ + 'id': key, + 'text': text, + 'blue_thumb': blueThumb, + 'red_thumb': redThumb, + }); + } + }); + } + + return data; + }); + } + + // add a new answer to the database, with his id, text, blue thumb and red thumb + Future addQuestion(String text, int blueThumb, int redThumb) { + final int questionId = DateTime.now().millisecondsSinceEpoch; + return _database.child('questions/$questionId').set({ + 'text': text, + 'blue_thumb': blueThumb, + 'red_thumb': redThumb, + }); + } + + // update the blue thumb of a question + Future updateBlueThumb(String id, int blueThumb) { + return _database.child('questions').child(id.toString()).update({ + 'blue_thumb': blueThumb, + }); + } + + // update the red thumb of a question + Future updateRedThumb(String id, int redThumb) { + return _database.child('questions').child(id.toString()).update({ + 'red_thumb': redThumb, + }); + } + + // delete a question + Future deleteQuestion(String id) { + return _database.child('questions').child(id.toString()).remove(); + } +} diff --git a/lib/main.dart b/lib/main.dart index a73134b..6be42b6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ -import 'package:chat_chuispt/question2.dart'; +import 'package:chat_chuispt/show_features_widget.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:chat_chuispt/database_service.dart'; import 'firebase_options.dart'; @@ -11,20 +13,30 @@ void main() async { options: DefaultFirebaseOptions.currentPlatform); runApp(MainApp()); } catch (e) { - print(e); + if (kDebugMode) { + print(e); + } } } class MainApp extends StatelessWidget { - MainApp({super.key}); - - final stream = DatabaseService().getTexts(); + const MainApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final stream = DatabaseService().getData(); + return MaterialApp( - home: Scaffold( - body: Center(child: TextListWidget(stream: stream)), - )); + home: Scaffold( + body: Column( + children: [ + Expanded( + child: DataListWidget(stream: stream), + ), + const AddQuestionWidget(), + ], + ), + ), + ); } } diff --git a/lib/question.dart b/lib/question.dart index a55c800..dfbd7e0 100644 --- a/lib/question.dart +++ b/lib/question.dart @@ -1,8 +1,3 @@ -import 'dart:math'; - -import 'package:firebase_database/firebase_database.dart'; -import 'package:flutter/foundation.dart'; - class LocalQuestion { int id = 0; String text = ""; @@ -60,130 +55,3 @@ class LocalQuestion { ); } } - -class QuestionList { - late List questions; - - QuestionList() { - questions = []; - loadQuestions(); - } - - Future loadQuestions() async { - final data = await _getData(); - - if (data != null) { - questions = - data.map((question) => LocalQuestion.fromJson(question)).toList(); - } - } - - Future>?> _getData() async { - try { - final database = FirebaseDatabase.instance; - final ref = database.ref("questions"); - - final snapshot = await ref.once(); - final value = snapshot.snapshot.value; - - if (value is List) { - final data = value - .map((item) => { - "id": item["id"], - "text": item["text"], - "blue_thumb": item["blue_thumb"], - "red_thumb": item["red_thumb"], - }) - .toList(); - - return data; - } else { - if (kDebugMode) { - print("Invalid data format"); - } - return null; - } - } catch (error) { - if (kDebugMode) { - print("Error loading questions: $error"); - } - return null; - } - } - - Future getQuestion() async { - final choices = []; - - for (final question in questions) { - final score = question.globalScore * 100; - - for (var i = 0; i < score; i++) { - choices.add(question); - } - } - - return choices.isEmpty ? null : choices[Random().nextInt(choices.length)]; - } - - Future getBlueThumb(int id) async { - try { - final database = FirebaseDatabase.instance; - final ref = database.ref().child("questions/$id/blue_thumb"); - - final snapshot = await ref.once(); - final value = snapshot.snapshot.value; - - return value as int?; - } catch (error) { - if (kDebugMode) { - print("Error getting blue thumb count: $error"); - } - return null; - } - } - - Future increaseBlueThumb(int id) async { - try { - final database = FirebaseDatabase.instance; - final ref = database.ref().child("questions/$id/blue_thumb"); - - final currentCount = await getBlueThumb(id) ?? 0; - await ref.set(currentCount + 1); - } catch (error) { - if (kDebugMode) { - print("Error increasing blue thumb count: $error"); - } - } - } - - Future getRedThumb(int id) async { - try { - final database = FirebaseDatabase.instance; - final ref = database.ref().child("questions/$id/red_thumb"); - - final snapshot = await ref.once(); - final value = snapshot.snapshot.value; - - return value as int?; - } catch (error) { - if (kDebugMode) { - print("Error getting red thumb count: $error"); - } - return null; - } - } - - Future increaseRedThumb(int id) async { - try { - final database = FirebaseDatabase.instance; - final ref = database.ref().child("questions/$id/red_thumb"); - - final currentCount = await getRedThumb(id) ?? 0; - await ref.set(currentCount + 1); - } catch (error) { - if (kDebugMode) { - print("Error increasing red thumb count: $error"); - } - } - } -} diff --git a/lib/question2.dart b/lib/question2.dart deleted file mode 100644 index 48fee89..0000000 --- a/lib/question2.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:firebase_database/firebase_database.dart'; -import 'package:flutter/material.dart'; - -class DatabaseService { - final DatabaseReference _database = FirebaseDatabase.instance.ref(''); - - Stream> getTexts() { - return _database.child('questions').onValue.map((event) { - final texts = []; - - final List? data = event.snapshot.value as List?; - if (data != null) { - for (final question in data) { - final text = question['text'] as String?; - if (text != null) { - texts.add(text); - } - } - } - - return texts; - }); - } -} - -class TextListWidget extends StatelessWidget { - final Stream> stream; - - const TextListWidget({Key? key, required this.stream}) : super(key: key); - - @override - Widget build(BuildContext context) { - return StreamBuilder>( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - final texts = snapshot.data!; - return ListView.builder( - itemCount: texts.length, - itemBuilder: (context, index) { - final text = texts[index]; - return ListTile( - title: Text(text), - ); - }, - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ); - } -} diff --git a/lib/show_features_widget.dart b/lib/show_features_widget.dart new file mode 100644 index 0000000..5adbcb9 --- /dev/null +++ b/lib/show_features_widget.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:chat_chuispt/database_service.dart'; + +class TextListWidget extends StatelessWidget { + final Stream> stream; + + const TextListWidget({Key? key, required this.stream}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StreamBuilder>( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + final texts = snapshot.data!; + return ListView.builder( + itemCount: texts.length, + itemBuilder: (context, index) { + final text = texts[index]; + return ListTile( + title: Text(text), + ); + }, + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } +} + +class DataListWidget extends StatelessWidget { + final Stream>> stream; + + const DataListWidget({Key? key, required this.stream}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StreamBuilder>>( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + final data = snapshot.data!; + return ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) { + final question = data[index]; + final id = question['id'] as String; + final text = question['text'] as String; + final blueThumb = question['blue_thumb'] as int; + final redThumb = question['red_thumb'] as int; + + return ListTile( + title: Text(text), + subtitle: Text('$blueThumb - $redThumb'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.thumb_up), + onPressed: () { + DatabaseService().updateBlueThumb(id, blueThumb + 1); + }, + ), + IconButton( + icon: const Icon(Icons.thumb_down), + onPressed: () { + DatabaseService().updateRedThumb(id, redThumb + 1); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + DatabaseService().deleteQuestion(id); + }, + ), + ], + ), + ); + }, + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } +} + +// The goal of this widget is to allow the user to add a new question to the database +// The user can add a question by typing it in the text field and clicking on the button +// The question is added to the database with the id, text, blue thumb and red thumb +class AddQuestionWidget extends StatefulWidget { + const AddQuestionWidget({Key? key}) : super(key: key); + + @override + _AddQuestionWidgetState createState() => _AddQuestionWidgetState(); +} + +class _AddQuestionWidgetState extends State { + final TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TextField( + controller: _controller, + ), + ElevatedButton( + onPressed: () { + DatabaseService().addQuestion( + _controller.text, + 0, + 0, + ); + }, + child: const Text('Add question'), + ), + ], + ); + } +} diff --git a/test/database_test.dart b/test/database_test.dart new file mode 100644 index 0000000..e69de29 diff --git a/test/question_test.dart b/test/question_test.dart index dc9e760..8b13789 100644 --- a/test/question_test.dart +++ b/test/question_test.dart @@ -1,31 +1 @@ -import 'dart:async'; -import 'package:chat_chuispt/question.dart'; -import 'package:firebase_database/firebase_database.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -class DataSnapshotMock extends Mock implements DataSnapshot { - DataSnapshotMock(this.value); - - @override - final dynamic value; -} - -class MockFirebaseDatabase extends Mock implements FirebaseDatabase {} - -void main() { - LocalQuestion localQuestionTest = LocalQuestion( - id: 1, - text: 'test', - blueThumb: 1, - redThumb: 1, - ); - - test('localQuestion Constructor', () { - expect(localQuestionTest.id, 1); - expect(localQuestionTest.text, 'test'); - expect(localQuestionTest.blueThumb, 1); - expect(localQuestionTest.redThumb, 1); - }); -} From 35f8c8baf0d3bdc84aaf1e2d069331076432d639 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH <49043100+robjo82@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:18:45 +0100 Subject: [PATCH 24/37] Just 2 imports than I delete during the merging --- lib/main.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/main.dart b/lib/main.dart index be22fa3..9ae840c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ import 'package:chat_chuispt/textstyle.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; import 'main_page.dart'; From 1d492d1aecd8807adc0b599e590001c9ac1eb5d6 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 00:41:12 +0100 Subject: [PATCH 25/37] logo picture --- android/app/build.gradle | 1 + android/app/src/main/AndroidManifest.xml | 2 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 2739 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 1707 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 3799 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 5628 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 7189 bytes assets/logo.png | Bin 0 -> 3061 bytes ios/Runner.xcodeproj/project.pbxproj | 2 +- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 88773 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 579 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 1333 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 2207 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 909 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 2129 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 3438 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 1333 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 3078 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 4726 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 4726 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 6821 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 2939 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 5831 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 6386 bytes lib/main page/main_page.dart | 85 ++++++++++++----- pubspec.lock | 88 ++++++++++++++++++ pubspec.yaml | 6 ++ 27 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 assets/logo.png diff --git a/android/app/build.gradle b/android/app/build.gradle index 2c3f56f..16419f0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -51,6 +51,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + minSdkVersion 21 } buildTypes { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c8747d7..4d7ccf0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ R>X#&&mMb? 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 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..1d67c066d87ca22539e821736c16faafdffd85f3 100644 GIT binary patch delta 1702 zcmV;X23h&K1FH>?8Gix*007uvZqNV#269P6K~#7F?U;E;Ra+FsuX)o z?8imyd9R*q;Nkk?uswI5eZKweZ+&YWm5M(sl)oE5=ja2ZDS!F^X^K8Tn&L+R85tQ3 z9Xj;u*RQW%ziw}D|80PDb#*;FJl3yYKYsjpEiEnXynp}x+_`fxF)?r7zWvpJCQX`@ zoSZyv+&IQJrc095(<`mSER+SAkX(xpqk4v@9Ab!KMfkRd}#N=jm5W3OMo zURPJwzkmN3Gk<1yd3h~avIMMi=gu8GcreVpc=6)q&6`L{W`ze414H}e|mIj)O z7cVYfz8reQ{pRLoUfkN+I&0Rf;lqcoTD3~`h@YQdMMcHp#f$IWz1#bMyuH1Rjg6l_ zf9~$?u0lhM9655&o;|#kab;y?YHDg}X({8WQ>V_KKYyQJb8~aoty>3qy#{FP*s-h# z4#ts@k?2O;A2DLY)~#Fl2^rPY)NqJ<_wH?MZ0xv)(9qD3kdX7|&y$Sl>FGZT$k5O* zE-r4(nl;+m+WbjwU%PgVfsT&OrcIlIgM$qW43J24bTnwtQ>VuPw|)C|^aQ5(_;~cx z)YK#&P=CLE{n*pYnKQw`0JZXNnBb=k8#VxfL4JNd3_W@BL_9KP%os>|_wJp)zkhvw zy|@__6$QJdrlt-K4q`tt0dX9lVc$sj*|TQ=Qn??*u-BCnOF zXAX~_J$qL0bmGJb=tTAM0Sy~AY}>YNtYvR+e}CuBozbI5cbWiO0u9FpADcIC=2#;l zBC@ly(apkz3(uT60|>?`DJkfh@%M}L01y_(UQeGs4H`AmrcIl6{P^*S6DLBXjg8IY z$B((WXwf2)E+HXd!h{J|u3Y(B3iVJl1A-W?YVtyC&Z3d8D?sRz4tMCwh8%T}{A^I1yD@c=hTP?m%%xEAV>Z!Uebi!J9X4 zmMvRWSXj7k-#%mxsaUHVK=<$8H#0NCZGUgyzO9fPNFf;km5W-zm|F3Dk^21kGuQC& zaAf}I(WBkFcPAz$vNw35HK0d0aNvNGlam}kNl8i0&dx|1ZSwAyFJB%zb__dt`0!ys zK!BPDi9?4DVS^kW1MCN41WG7$babScbR{NrfJo`Gva%^trtI3a3#n4P*x!*OM}N>S zC-ULL2lbekP*6}nW>ERX9Zr+YOrAX1!oorhAc7W$27^pNNhkx+&~=d8R48PtudmM) zUv6k<5c6qHgm}9OWX@FuvB!XDDzIY;r;m>hc`KY30U?N^M~`xFFh_#{g?V{-VPRod zx%xb!f#FJ(l?jN<2Mvw~!{+AZB!38;tzEnJ!Gi~|LM@|)A3l5-k`z+X!5rO^sBzy1 zG$Q~8h+QTidS`{C1}iJ8jT<*ow;2={7n3QZ&!oE_)fwSAs7#% z54myUhMG5O0E8J*sIemC3?Ok6=cRrj7mn=TRg_9RhBTzw0|gCf+)erqO{g zP5m1+cZ{4AO^>auEfPUPs(RXKA5C`w5%hZ`6qXG#YJU@GM6Kn>_(nT< z@?^)!xECc1=EwPr8`=R|pVIqC)nCkr>6HEp%{ zBKRqQR8b=Z13E*|0-$Jm){b*%TQT&WqLB%xV-yt?p;;&)4f$v!=jkxf9G)p}!QT0Y wRiPBUvef_XKWM!keII}{MIRtd@y7xB2XLW`@_L=83IG5A07*qoM6N<$f-@p9EdT%j literal 442 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 bZ!zjBWtiy^aZ(g$SV1>*O?2wCoMQe1-AX(m literal 721 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!iOI#yLg7ec#$`gxH85~pclTsBt za}(23gHjVyDhp4h+5i=O3-AeX1=1l$e`s#|#^}+&7(N@w0CIr{$Oe+Uk^K-ZP~83C zcc@hG6rikF&NPT(23>y!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..30bcf898e3cb3f3f4fa1644adf050fd59f1a3e28 100644 GIT binary patch literal 5628 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372eebdb28e45604e46eeda8dd24651419bc0..d5ef4cf4b37220d2424f0a6eeb53b97e4320dd3e 100644 GIT binary patch literal 7189 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 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..83482e456acef323a22a14a1ef26c22e44b3afd3 GIT binary patch literal 3061 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 literal 10932 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 DBgPu| delta 279 zcmV+y0qFk21g8R!8Gi!+006pI?LPnj0Blf9R7L;)|5U~J`u_j-{Qm)0oAmqtj@kOz z^8J|I`-|B6ht~R5kG+%I`zf~eztraM`u^bc{`dO)zUlmg)%x%C`E}6wSI77~z4s`y z^XT{f(eM4n?EUff`e@AgO~UxV*5*r_%Uhbj5N)LaQj!wdIe!-b004GLL_t&-)18pX z4udcZ1u-#g(~z+5JN*AY5?>Gw7hsN~k)CYt4dQDFxbs5*_&e@Hj)wtt(&JE<3Eq*D z;_gQLvqXoKv=I*gWqM9C(Tvu0>=?hTbOp9!6k6AF;>f6|S5%jGEE}TA9h)e`Yuiu8 d7)l?o1NFcJg%EAfM$P~L002ovPDHLkV1hgXnxg;! diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 797d452e458972bab9d994556c8305db4c827017..52a7ad867042b2cbba5400202459a43e21c761dd 100644 GIT binary patch delta 1325 zcmV+|1=9MK1GNf}8Gix*000A=FFF7K1pG-vK~#7F?UrdsRb3c`&6EsNLGXt{ghPUw zeIjPzYDCQqH_QKeS3TR)q|<2sj;!KQBhHU3s_lM`R&`crlzJZUc4wMC}?YI+qG+# zrKRPyYu6$pBXxCkM@L6jR#v`#{aRjL{_WehEd$QU$vJfBP*_;l?c2A-g98Ht2M-mG8;lrh+ zCE@!|pFWk8l-#|0*U!)I)~#DqXfwd)&!5lB%L8m|Y=2BuRTT|0n3a`9P7J1`q>$O% z+#G|f49a1b`(M9)UAlD1+S)oWFfb-2W_ET~I$&}o#i600)2C0L5ykTIGUMp$!-o${ z{pHIS9(8ndP_>7L2PJv^`ZXywH#f7Iw6wJ3P)zuQ;-n@Ae z<`7*|Qxhz9baX^VM`vbcc6D`GSy{Qdx+W$jdVhO+^QAzC6u=4!3S9rB3!KE)*LP}a zihJx6f*(A1aOKLCix)4xdGqGUlP47w6_^a-K=U6zev|^ZzrUZWo}QlYDC{D6d3oWn zwzgJ46#_7>FJHa{5=OC(_V#u%h6pr>LM8Gg8TkGC_mh*82nS`7cW!Pj@*&aC&`=1L z_BreG5*Dk?sE z_H6aX>|o*M`}gmCeSP9Op#a2n`0!zg5P#gqEFU6WTwK_-@CP<|V-aMAj5Rei5!8<# zKX53tCUFJ`Vnd1v+9)tCsIIR5^XE@}eSiJ>`g-^S5|y4icP=R@2~@;9JUlEXCx?cS z^zSq5+qaLJBX#M334ViY(k&_~Vs|<_J7d34Ff%i=d-rZC3=wRnJ$v>@1P%xYKoE}} zJ(3!DVq$_-)B9{}Y^ZsxC~zn|tFp2Z?-bm7@7}$?|Lf@Jh^7#lVBpdL^V`XjCx5X$ zx)4sp5rAsbnrIT(vW#fEqx0b4V0f9IpO+Dt8dubSeNInLv&uhBz7{YhU}tAXuLX>$ zX*&_35Z+P)$kx`@?(Xi!FR8zfy{xP(PC}CgyR)-%Gr-V8XGKkvD$-3VM5c0T!e3ilf%CW59J6s~t00000NkvXXu0mjf;A(z0 delta 390 zcmV;10eSwl3YG(q8Gi!+006rnNM8T|0E$pdR7L;)|5U~J0au$Tw)XJ){%+3s=lA~6 z@BMVp`S<<*VaoaP`~U3u{%g(ou*=|m)B4`@{`33)?ezIj#Q6OF|6IuUF}e2O>+>eB z?J{?+FLkYu+4_Uk`r_>LHF~flZm0oBf#vr8%vJ>#p~!KNvqGG3)|f1T_)ydeh8$vDceZ>oNbH^|*hJ*t?Yc*1`WB&W>VYVEzu) zq#7;;VjO)t*nbgf(!`OXJBr45rP>>AQr$6c7slJWvbpNW@KTwna6d?PP>hvXCcp=4 zF;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f{{wsZvX%Q diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 6ed2d933e1120817fe9182483a228007b18ab6ae..974672c03ee979837792a0c89c19735d1f4d3659 100644 GIT binary patch delta 2206 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 X?6bJ34{@w}00000NkvXXu0mjfBFe#m delta 266 zcmV+l0rmck2buzq8Gi!+003c4mpuRg09{Z_R7L;)|5U~JDYo_jSDX9(|7FYh`2GLd z^Zv2r{H^2sT*&w!Y^SB+`<>qVZqE6)=lqo0`vF#&*75!I`TIh@_d&k*HoEtQyV-iD z%Xz2D9EQRbeYh5Nr~y=#0ZD;^+vz0$004MNL_t(2&&|%+4u6C&2tZM$Wf&dzefR%A z(^3-?6X>hnCz2Ba@RH&`m!pgy?n@#@AuLYB&}Q)FGY`?vcft0!vht0Z@M&ZeNCWXh75gzRTXR8EE3oN&6 Q00000NkvXXt^-0~g6%4Wh5!Hn diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index fe730945a01f64a61e2235dbe3f45b08f7729182..8e0a9f9ffab31879fb5989ebcd1c19087b9b60d6 100644 GIT binary patch delta 2127 zcmV-V2(b6g1JMwW8Gix*003^;-G2Z82pCC3K~#7F?U{E>6-yY!7riQWMTlYpVvVt& zLO|>qjMzoxS+HWP#NK-sPyt(PD2hEsu>uN~X!HRZHP%>T7mdbV(5UbCX7V;0@ZGy` z_xfVq{rAQFD zPEAc^U7eksn}0QH)~{c`vSrH#2M0HA-h9ZAAtg$bU>}}6dv@{S#Rm@_{A#$hYSr4d zZ5vphKY!l4ckd%dj%a(6lauGoo40-Y_KqDpmMd58z<~qW-t*_rFIlpL$A2B%wQJY1 zTI@0ldHwqJEWIu+E_3J3ZP%`y+Ct|2`0*oe-n?AbtAAH->(;H4Cr|Fvr_Y587i;%KP{4t5vH8p~sIOU!+Kpnl)=4J$lsB z(^Ee;atyarsZvmO(4awzNdMKVS8IDqmoB|<;ljRs`{v1$M-iJoV#J8kr%$T`Zrr#r zXU?2;>wng5+O)~v-@jzZl6+W`CQZJ5`(`j))*~V!V&K4m=&Ni=OiTo^yoMMkz_e-8 ziWMuyEh)wI`t|D}K=!GgK7ASx5U^s!3R2L|&u`?&k&%&+M#8OFu_DRDRw^16Ub=Ki z_VUVo`}QFSTz~oU1-T{9wBiMMGBY#P@dV2`bbshj6tH#c))OX7SiXGu*RNlVfy=rc zK71IgphT@^&6;)cPj@Ks(EDpY9Fq)99@gxz>PCsNBZi5C5_&s&%6uV}AWbxv~ z_wV1|zI}UulKZZ%uF=uaWL|7+tQ^1^2L=ZA=+Oh*7`NKBYqR!J!X7?+XbjvcRjTl- zr}oU8ITN|S5{w!(DlIK-%$PA#rcBweVFO!Fms+I}T9jDQnp;`tc*+>Kcq@MOSbwS) zFJ1tP;w-fmj&9tz5vgW55f&C6Hs88+i!RDow{G2P)Tn{B#ozI+)z zki6PPnHliU;^WFcG6!oB2iyX-27kSy;3=Y5P_gURuTSzJ&j!Ffc<>-^@)B|G+&S3@ z^O&92dw6)D{CYscS87}T{{109OiT=dAZ78xhYxHE6rwaDn)mPDw+il#9XoI>6dpF3 zv%}}lpJ_y@sJeib%Aeho{WRb-nFqg7*)nCysO`{z$=L+F&gRXVt%8f94 zyLa#Y>eVaSf!L-^o3?G+Mny%jY_!y^TNk&Ea%ms0TD2;lYW@}EC@M)AN3G3|)K!Gz z;^H72WxZ9aRwOmaj@t7XbR9Z$h=T)Oj4D7uf#Q@dd3HdCVGC|cb#P`vCWBk}Rx{_*2Sx;QCdEhw!(4;BGa*%pYz4w#2aO6V*Wdf{SL6mdR-zLYCK z;A5!xEa$|D6LE(0lI#eDTqlmi(17Eyp2-=h2$8lpUN2m%EfnPN&42M>_UzfHCiD#o z3gVQ@)(;ytjEu%^V*?&PevDkusB>m`#c7!fQr6-TNH1Jy%1IFEmA8)`JwhTl0&s9Z zG5kcJaa1f+PtxP>*-a+C0de@ZX{izH7#CNsUPX^B!q5vB?+je3B?^Gsl8q=o9{?XH zp?Dh9UVEmcz><6z1Aj$q4&^|$%k*#Bv?Tcje*M=;IohgB)vt87V7$u zPcb62gDY3AOa07eVCDFbyO-u3 zZua0jgP%lzp$ZPcU4(YP@#Du)4DH{YauEjq=LfMvgGj;Ms8J)-7jD247>~*1*ZTR5 z|3_73W@fwy!)I3h*Z)`h1!0G4%do?>W!T}`GJZ3-e*vsDGNcvP39JAB002ovPDHLk FV1koKCdU8( delta 447 zcmV;w0YLuI5Y7XT8Gi!+007oyx*7lg0G3cpR7L;)|5U~J0au$Tw)URh`@-w}Xw3Np zS)Ix4{k7)&ujKrh-TO(x_}20L&+q+}+xr1ilg8}*yXgGl_5RcF{f*iBEV%Z~-t4>5 ziGV;=={^- z?sLQGb)?A{hr$_!z8HbH7kH=vM0x-*R~t>;jsO4v^GQTOR7l6|(&r9>FcgO2dg?%> z;=sK?5%;?Pn^T7LL?Y$@5u?06NuIR*0?Yf$Hf5Afk+lM<^ch*jvO$sU*m9J?JI7eI zGFV6+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9R%3*Q+)t%S!MU_`id^@& zY{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&p6kME1_Z%?`+u)^el0!1<0sd p?Eyu!OMLDifi)An*I;?S-wj=m4RYIt!kPd8002ovPDHLkV1itX^ilu- diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 321773cd857a8a0f0c9c7d3dc3f5ff4fb298dc10..c89e50565c344930a266adf4aea05320f4c5d06f 100644 GIT binary patch literal 3438 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 Zxpx7IC@PekH?;r&002ovPDHLkV1lP-YnT84 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 797d452e458972bab9d994556c8305db4c827017..52a7ad867042b2cbba5400202459a43e21c761dd 100644 GIT binary patch delta 1325 zcmV+|1=9MK1GNf}8Gix*000A=FFF7K1pG-vK~#7F?UrdsRb3c`&6EsNLGXt{ghPUw zeIjPzYDCQqH_QKeS3TR)q|<2sj;!KQBhHU3s_lM`R&`crlzJZUc4wMC}?YI+qG+# zrKRPyYu6$pBXxCkM@L6jR#v`#{aRjL{_WehEd$QU$vJfBP*_;l?c2A-g98Ht2M-mG8;lrh+ zCE@!|pFWk8l-#|0*U!)I)~#DqXfwd)&!5lB%L8m|Y=2BuRTT|0n3a`9P7J1`q>$O% z+#G|f49a1b`(M9)UAlD1+S)oWFfb-2W_ET~I$&}o#i600)2C0L5ykTIGUMp$!-o${ z{pHIS9(8ndP_>7L2PJv^`ZXywH#f7Iw6wJ3P)zuQ;-n@Ae z<`7*|Qxhz9baX^VM`vbcc6D`GSy{Qdx+W$jdVhO+^QAzC6u=4!3S9rB3!KE)*LP}a zihJx6f*(A1aOKLCix)4xdGqGUlP47w6_^a-K=U6zev|^ZzrUZWo}QlYDC{D6d3oWn zwzgJ46#_7>FJHa{5=OC(_V#u%h6pr>LM8Gg8TkGC_mh*82nS`7cW!Pj@*&aC&`=1L z_BreG5*Dk?sE z_H6aX>|o*M`}gmCeSP9Op#a2n`0!zg5P#gqEFU6WTwK_-@CP<|V-aMAj5Rei5!8<# zKX53tCUFJ`Vnd1v+9)tCsIIR5^XE@}eSiJ>`g-^S5|y4icP=R@2~@;9JUlEXCx?cS z^zSq5+qaLJBX#M334ViY(k&_~Vs|<_J7d34Ff%i=d-rZC3=wRnJ$v>@1P%xYKoE}} zJ(3!DVq$_-)B9{}Y^ZsxC~zn|tFp2Z?-bm7@7}$?|Lf@Jh^7#lVBpdL^V`XjCx5X$ zx)4sp5rAsbnrIT(vW#fEqx0b4V0f9IpO+Dt8dubSeNInLv&uhBz7{YhU}tAXuLX>$ zX*&_35Z+P)$kx`@?(Xi!FR8zfy{xP(PC}CgyR)-%Gr-V8XGKkvD$-3VM5c0T!e3ilf%CW59J6s~t00000NkvXXu0mjf;A(z0 delta 390 zcmV;10eSwl3YG(q8Gi!+006rnNM8T|0E$pdR7L;)|5U~J0au$Tw)XJ){%+3s=lA~6 z@BMVp`S<<*VaoaP`~U3u{%g(ou*=|m)B4`@{`33)?ezIj#Q6OF|6IuUF}e2O>+>eB z?J{?+FLkYu+4_Uk`r_>LHF~flZm0oBf#vr8%vJ>#p~!KNvqGG3)|f1T_)ydeh8$vDceZ>oNbH^|*hJ*t?Yc*1`WB&W>VYVEzu) zq#7;;VjO)t*nbgf(!`OXJBr45rP>>AQr$6c7slJWvbpNW@KTwna6d?PP>hvXCcp=4 zF;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f{{wsZvX%Q diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 502f463a9bc882b461c96aadf492d1729e49e725..0f7054dc4aec796c645df395f4dbb9d39a0bc538 100644 GIT binary patch literal 3078 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 KMNUMnLSTZ!%{3bU diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 0ec303439225b78712f49115768196d8d76f6790..6b2472540ba60b8652addb1c43e231a9de2122a3 100644 GIT binary patch literal 4726 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 fhb{l>x3*DXjtZ delta 850 zcmV-Y1Fih_B;E#)8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$g2>{^H2?qr diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 0ec303439225b78712f49115768196d8d76f6790..6b2472540ba60b8652addb1c43e231a9de2122a3 100644 GIT binary patch literal 4726 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 fhb{l>x3*DXjtZ delta 850 zcmV-Y1Fih_B;E#)8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$g2>{^H2?qr diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index e9f5fea27c705180eb716271f41b582e76dcbd90..038a2130bf32e23efd6f6f270efc4bf64ce7f500 100644 GIT binary patch literal 6821 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 delta 1668 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 delta 749 zcmVg;Ps8|O$@u8^{Z_{KM!@$5TAfS6_e#O{MZfpz`2O`0$7~@NRr(1{THzH08y3x{{PYM{eL;T_A9^tcF_4Sxb`8l z_9V3RD6;a(-0A^Pjsi!1?)d#Ap4Tk3^CP0(07;VpJ7@tgQ}z4)*zx@&yZwC9`DV-b z0ZobH_5IB4{KxD3;p_6%|f=bdFhu+F!zMZ2UFj;GUKX7tI;hv3{q~!*pMj75WP_c}> z6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FMs~w_u?Av_yNBmRxVYrpi(M% zFMP21g+hmocQp3ay*Su=qM6He)*HaaTg$E^sym`(t%s3A)x!M+vfjXUBEpK6X9%iU zU!u9jj3(-$dM~sJ%Liy#?|+!6IY#MTau#O6vVj`yh_7%Ni!?!VS+MPTO(_fG+1<#p zqu;A#i+_(N%CmVnYvb>#nA{>Q%3E`Ds7<~jZMywn@h2t>G-LrYy7?Dj{aZqhQd6tzX%(Trn+ z)HNF}%-F{rr=m*0{=a;s#YDL00000NkvXXu0mjf*8G_? diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 8953cba09064923c5daf2d37e7c3c836ccdd794b..ed505f6c15845252da1ffca85cae8a8bf9a9db77 100644 GIT binary patch literal 5831 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 KMl`}7#{LhXdHIY0 delta 1217 zcmV;y1U~!6Ey@Xy8Gi!+001u>&=UXv0SHh`R7L;)|5U~JDYo_jSDRDC`1<|-SjPDL z{{Q{{{{H{}09Kk-#rR9Y_viNgVafPO!S|ls`uzR=MZfp^{QU=8od8La1X`Tr_Wmff z_5e$ivgQ1@=KMy$_g9a+`TPAle6cOJ_Fc#L7qIpvwDkd1mw$fK`6IOUD75rX!}mad zv(fMTE4=(Nx%L54lL1hVF1YpqNrC`FddBPg#_Ietx%Lrkq5wX00X1L{S%Cm9QY*av z#_Rh5PKy9KYTWbvz3BX9%J>0Hi1+#X{rLA{m%$Kamk?i!03AC38#Yrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`?TG`AHia671e^vgmp!llK zp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?tc*y?iZ$PR7_ceEIapF3KB14K0Pog?7wtd+^xgUCa_GVmlD z<^nU>AU_Yn-JU?NFdu|wf^bTCNf-wSBYVZltDdvGBln-YrbeGvJ!|s{#`gjN@yAMb zM6cjFz0eFECCsc|_8hTa3*9-JQGehksdoVP^K4m?&wpA~+|b%{EP5D-+7h)6CE; z*{>BP=GRR3Ea}xyV*bqry{l^J=0#DaC4ej;1qs8_by?H6Tr@7hl>UKNZt)^B&yl;)&oqzLg zcfZxpE?3k%_iTOVywh%`XVN-E#COl+($9{v(pqSQcrz=)>G!!3HeNxbXGM@})1|9g zG4*@(OBaMvY0P0_TfMFPh fVHk#CZX3S=^^2mI>Ux-D00000NkvXXu0mjfus4I= diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 0467bf12aa4d28f374bb26596605a46dcbb3e7c8..db450682a045b00e4d84f335f9eee79b03d38e34 100644 GIT binary patch literal 6386 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 NQCHShs#dTL`yV>ho<{%x delta 1410 zcmV-|1%3MRF^UV28Gi!+002YCyxsr+0P|2xR7L;)|5U~JDYo_jSDRDB`2GI>{Qds= z{r_0T`1}6fwc-8!#-TGX}_?g)CZq4{k!uZ_g@DrQdoW0kI zu+W69&uN^)W`CK&06mMNcYMVF00dG=L_t(|+U?wHQxh>12H+Dm+1+fh+IF>G0SjJM zkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJTkdTm&kdTm&kdTm&kdP`e zsgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>VI$fQI%^ugM`#6By?GeadWcu z0gy9!D`m!H>Bd!JW(@avE8`|5XX(0PN}!8K>`dkavs;rHL+wy96QGNT=S@#7%xtlm zIW!++@*2zm-Py#Zr`DzqsLm!b{iskFNULSqE9A>SqHem>o31A%XL>S_5?=;V_i_y+ z(xxXhnt#r-l1Y8_*h`r?8Tr|)(RAiO)4jQR`13X0mx07C&p@KBP_2s``KEhv^|*8c z$$_T(v6^1Ig=#R}sE{vjA?ErGDZGUsyoJuWdJMc7Nb1^KF)-u<7q zPy$=;)0>vuWuK2hQhswLf!9yg`88u&eBbR8uhod?Nw09AXH}-#qOLLxeT2%C;R)QQ$Za#qp~cM&YVmS4i-*Fpd!cC zBXc?(4wcg>sHmXGd^VdE<5QX{Kyz$;$sCPl(_*-P2Iw?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF34$0Z;QO!J zOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUCUoZo%k(yku QW&i*H07*qoM6N<$f+2{_S^xk5 diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index aa4afd3..b679d01 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -25,34 +25,30 @@ class _MainPageState extends State { var appState = context.watch(); // état de l'application MyTextField textField = const MyTextField(); // le champ de texte LeftBar leftBar = const LeftBar(); // la barre de gauche + MyDrawer myDrawer = const MyDrawer(); // le drawer return Consumer(builder: (context, value, child) { return Scaffold( - body: Row( - children: [ - leftBar, - if (appState.questionsList.isEmpty) - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - const Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), - ], - ), + //drawer: myDrawer, + body: appState.questionsList.isEmpty + ? Container( + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + const Expanded(child: QuestionGrid()), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), + ], ), ) - else - Expanded( + : Expanded( child: Container( color: themeApp.colorScheme.background, child: Column(children: [ @@ -65,8 +61,6 @@ class _MainPageState extends State { ]), ), ), - ], - ), ); }); } @@ -101,3 +95,44 @@ class _LeftBarState extends State { )); } } + +class MyDrawer extends StatefulWidget { + const MyDrawer({Key? key}) : super(key: key); + + @override + _MyDrawerState createState() => _MyDrawerState(); +} + +class _MyDrawerState extends State { + final List _items = ['Item 1', 'Item 2', 'Item 3']; + + /*void _addItem() { + setState(() { + _items.add('Item ${_items.length + 1}'); + }); + }*/ + + @override + Widget build(BuildContext context) { + return Drawer( + child: ListView.builder( + itemCount: _items.length + 1, + itemBuilder: (BuildContext context, int index) { + if (index == 0) { + return ListTile( + title: const Text('Add item'), + onTap: () { + //_addItem(); + }); + } + return ListTile( + title: Text(_items[index - 1]), + onTap: () { + // Do something when the item is tapped + }, + ); + }, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 736ce01..2e73bc0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" + source: hosted + version: "3.3.6" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: @@ -41,6 +57,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" fake_async: dependency: transitive description: @@ -54,6 +86,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "559c600f056e7c704bd843723c21e01b5fba47e8824bd02422165bcc02a5de1d" + url: "https://pub.dev" + source: hosted + version: "0.9.3" flutter_lints: dependency: "direct dev" description: @@ -67,6 +107,14 @@ packages: description: flutter source: sdk version: "0.0.0" + image: + dependency: transitive + description: + name: image + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" + source: hosted + version: "3.3.0" js: dependency: transitive description: @@ -123,6 +171,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + url: "https://pub.dev" + source: hosted + version: "3.7.2" provider: dependency: "direct main" description: @@ -184,6 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.16" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" vector_math: dependency: transitive description: @@ -192,6 +264,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=2.18.0 <3.0.0" flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4b765ca..0c5eab7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,12 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 + flutter_launcher_icons: ^0.9.2 + +flutter_icons: + android: true + ios: true + image_path: "assets/logo.png" flutter: uses-material-design: true From 1d1225bbf11ad1d02889f7c981dfc7ef829d1044 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 00:46:55 +0100 Subject: [PATCH 26/37] bug fix --- lib/main page/main_page.dart | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index b679d01..a833ef8 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -48,18 +48,15 @@ class _MainPageState extends State { ], ), ) - : Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding( - padding: const EdgeInsets.all(20), child: textField), - ]), - ), + : Container( + color: themeApp.colorScheme.background, + child: Column(children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + const Flexible(child: History()), + Padding(padding: const EdgeInsets.all(20), child: textField), + ]), ), ); }); From 9aea6c6193420e200aabcdd0a8c82f72dc6ddee8 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 01:11:32 +0100 Subject: [PATCH 27/37] drawer made --- lib/main page/main_page.dart | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index a833ef8..befc7ec 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -24,20 +24,28 @@ class _MainPageState extends State { // * Variables : var appState = context.watch(); // état de l'application MyTextField textField = const MyTextField(); // le champ de texte - LeftBar leftBar = const LeftBar(); // la barre de gauche MyDrawer myDrawer = const MyDrawer(); // le drawer return Consumer(builder: (context, value, child) { return Scaffold( - //drawer: myDrawer, + appBar: AppBar( + title: Text('Chat ChuisPT', style: titleText), + backgroundColor: themeApp.colorScheme.primaryContainer, + elevation: 5, + leading: Builder(builder: (context) { + return IconButton( + icon: const Icon(Icons.menu), + onPressed: () => + Scaffold.of(context).openDrawer(), // open the drawer), + ); + })), + drawer: myDrawer, body: appState.questionsList.isEmpty ? Container( color: themeApp.colorScheme.background, child: Column( children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), + const SizedBox(height: 25), Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), const Expanded(child: QuestionGrid()), @@ -51,8 +59,6 @@ class _MainPageState extends State { : Container( color: themeApp.colorScheme.background, child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), const SizedBox(height: 25), const Flexible(child: History()), Padding(padding: const EdgeInsets.all(20), child: textField), @@ -103,23 +109,24 @@ class MyDrawer extends StatefulWidget { class _MyDrawerState extends State { final List _items = ['Item 1', 'Item 2', 'Item 3']; - /*void _addItem() { + void _addItem() { setState(() { _items.add('Item ${_items.length + 1}'); }); - }*/ + } @override Widget build(BuildContext context) { return Drawer( child: ListView.builder( + padding: const EdgeInsets.all(10), itemCount: _items.length + 1, itemBuilder: (BuildContext context, int index) { if (index == 0) { return ListTile( title: const Text('Add item'), onTap: () { - //_addItem(); + _addItem(); }); } return ListTile( From abdcbf0b49c183f189d2687350e5651528a2f0cb Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 01:32:16 +0100 Subject: [PATCH 28/37] Thumbs buttons added --- lib/main page/components/History.dart | 66 ++++++++++++++++++++------- lib/main page/main_page.dart | 6 ++- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index 85834ec..283592f 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -14,7 +14,13 @@ class History extends StatefulWidget { class _HistoryState extends State { final _key = GlobalKey(); - List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; + List reponseList = [ + 'oui', + 'non', + 'peut-être', + 'je ne sais pas', + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + ]; @override Widget build(BuildContext context) { @@ -32,26 +38,52 @@ class _HistoryState extends State { const SizedBox(height: 10), // * QUESTION * // - Container( - color: themeApp.colorScheme.primaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - question, - style: titleText2, - textAlign: TextAlign.center, - ), + Row( + children: [ + Expanded( + child: Container( + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + ), + ), + ), + ], ), const SizedBox(height: 10), // * REPONSES * // - Container( - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: titleText2, - textAlign: TextAlign.center, - ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Container( + color: themeApp.colorScheme.secondaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + reponseList[Random().nextInt(reponseList.length)], + style: titleText2, + textAlign: TextAlign.center, + ), + ), + ), + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_up, + color: themeApp.colorScheme.onPrimaryContainer, + )), + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_down, + color: themeApp.colorScheme.onPrimaryContainer, + )), + ], ), ], ), diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index befc7ec..a366c64 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -42,6 +42,7 @@ class _MainPageState extends State { drawer: myDrawer, body: appState.questionsList.isEmpty ? Container( + // ! si la liste de questions est vide color: themeApp.colorScheme.background, child: Column( children: [ @@ -57,9 +58,9 @@ class _MainPageState extends State { ), ) : Container( + // ! si la liste de questions n'est pas vide color: themeApp.colorScheme.background, child: Column(children: [ - const SizedBox(height: 25), const Flexible(child: History()), Padding(padding: const EdgeInsets.all(20), child: textField), ]), @@ -119,9 +120,10 @@ class _MyDrawerState extends State { Widget build(BuildContext context) { return Drawer( child: ListView.builder( - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.only(top: 50), itemCount: _items.length + 1, itemBuilder: (BuildContext context, int index) { + // if (index == 0) { return ListTile( title: const Text('Add item'), From 7a998dca4bf952e6859efcb8d4ff19a111e86937 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 11:28:25 +0100 Subject: [PATCH 29/37] Drawer upgrades --- lib/main page/main_page.dart | 109 +++++++++++++++-------------------- 1 file changed, 46 insertions(+), 63 deletions(-) diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index a366c64..cc7c2a7 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -24,7 +24,7 @@ class _MainPageState extends State { // * Variables : var appState = context.watch(); // état de l'application MyTextField textField = const MyTextField(); // le champ de texte - MyDrawer myDrawer = const MyDrawer(); // le drawer + MyDrawer myDrawer = MyDrawer(); // le drawer return Consumer(builder: (context, value, child) { return Scaffold( @@ -70,74 +70,57 @@ class _MainPageState extends State { } } -class LeftBar extends StatefulWidget { - const LeftBar({Key? key}) : super(key: key); - - @override - State createState() => _LeftBarState(); -} - -class _LeftBarState extends State { - @override - Widget build(BuildContext context) { - return Container( - color: themeApp.colorScheme.surface, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context.read().clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) +class MyDrawer extends StatelessWidget { + void _showContributionDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Contribution'), + content: Text('Merci pour votre contribution!'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Fermer'), + ), ], - )); - } -} - -class MyDrawer extends StatefulWidget { - const MyDrawer({Key? key}) : super(key: key); - - @override - _MyDrawerState createState() => _MyDrawerState(); -} - -class _MyDrawerState extends State { - final List _items = ['Item 1', 'Item 2', 'Item 3']; - - void _addItem() { - setState(() { - _items.add('Item ${_items.length + 1}'); - }); + ); + }, + ); } @override Widget build(BuildContext context) { return Drawer( - child: ListView.builder( - padding: const EdgeInsets.only(top: 50), - itemCount: _items.length + 1, - itemBuilder: (BuildContext context, int index) { - // - if (index == 0) { - return ListTile( - title: const Text('Add item'), - onTap: () { - _addItem(); - }); - } - return ListTile( - title: Text(_items[index - 1]), - onTap: () { - // Do something when the item is tapped - }, - ); - }, + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration(color: themeApp.colorScheme.background), + child: Text('Menu', style: titleText), + ), + ListTile( + title: Row( + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); // Ferme le drawer + _showContributionDialog(context); + }, + child: Row( + children: [ + const Icon(Icons.add_circle_outline), + const SizedBox(width: 25), + Text("Contribuer", + style: titleText2.copyWith(color: Colors.black)), + ], + )) + ], + ), + ), + ], ), ); } From 896a1bbc630fbc6907f50d44539964d132c49e81 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Sat, 25 Mar 2023 21:20:46 +0100 Subject: [PATCH 30/37] drawer file added --- lib/main page/components/Drawer.dart | 60 ++++++++++++++++++++++++++++ lib/main page/main_page.dart | 57 +------------------------- 2 files changed, 61 insertions(+), 56 deletions(-) create mode 100644 lib/main page/components/Drawer.dart diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart new file mode 100644 index 0000000..394f64d --- /dev/null +++ b/lib/main page/components/Drawer.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import '../../main.dart'; +import '../../constants.dart'; + +class MyDrawer extends StatelessWidget { + void _showContributionDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Contribution'), + content: Text('Merci pour votre contribution!'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Fermer'), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration(color: themeApp.colorScheme.background), + child: Text('Menu', style: titleText), + ), + ListTile( + title: Row( + children: [ + TextButton( + onPressed: () { + Navigator.pop(context); // Ferme le drawer + _showContributionDialog(context); + }, + child: Row( + children: [ + const Icon(Icons.add_circle_outline), + const SizedBox(width: 25), + Text("Contribuer", + style: titleText2.copyWith(color: Colors.black)), + ], + )) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index cc7c2a7..cf269e0 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -7,6 +7,7 @@ import '../main.dart'; import 'components/History.dart'; import 'components/QuestionsGrid.dart'; import 'components/TextField.dart'; +import 'components/Drawer.dart'; class MainPage extends StatefulWidget { const MainPage({Key? key}) : super(key: key); @@ -69,59 +70,3 @@ class _MainPageState extends State { }); } } - -class MyDrawer extends StatelessWidget { - void _showContributionDialog(BuildContext context) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('Contribution'), - content: Text('Merci pour votre contribution!'), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text('Fermer'), - ), - ], - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return Drawer( - child: ListView( - padding: EdgeInsets.zero, - children: [ - DrawerHeader( - decoration: BoxDecoration(color: themeApp.colorScheme.background), - child: Text('Menu', style: titleText), - ), - ListTile( - title: Row( - children: [ - TextButton( - onPressed: () { - Navigator.pop(context); // Ferme le drawer - _showContributionDialog(context); - }, - child: Row( - children: [ - const Icon(Icons.add_circle_outline), - const SizedBox(width: 25), - Text("Contribuer", - style: titleText2.copyWith(color: Colors.black)), - ], - )) - ], - ), - ), - ], - ), - ); - } -} From 92f59f4f04817cccacf6b6c0d7e2ab77fc9fa1a3 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 27 Mar 2023 17:31:35 +0200 Subject: [PATCH 31/37] no change --- lib/constants.dart | 2 +- lib/main page/components/Drawer.dart | 53 +++++++++++++++++--- lib/main page/components/History.dart | 60 +++++++++++++--------- lib/main page/components/TextField.dart | 66 +++++++++++++++++-------- lib/main page/main_page.dart | 57 ++++++++++----------- pubspec.lock | 10 +++- pubspec.yaml | 3 ++ 7 files changed, 169 insertions(+), 82 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index e0d7ab4..325928b 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -30,7 +30,7 @@ ThemeData themeApp = ThemeData( // ? SURFACE ? // surface: Colors.blueGrey.shade900, - background: Colors.blueGrey.shade600, + background: Colors.blueGrey.shade400, // ? ON ? // onPrimary: Colors.white, diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart index 394f64d..05c24ec 100644 --- a/lib/main page/components/Drawer.dart +++ b/lib/main page/components/Drawer.dart @@ -4,19 +4,57 @@ import '../../main.dart'; import '../../constants.dart'; class MyDrawer extends StatelessWidget { + const MyDrawer({Key? key}) : super(key: key); + + //! dialog window of remerciement + void _showThankYouDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Merci !'), + content: const Text('Merci pour votre contribution!'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Fermer'), + ), + ], + ); + }, + ); + } + + //! dialog window of contribution void _showContributionDialog(BuildContext context) { + final TextEditingController _controller = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: Text('Contribution'), - content: Text('Merci pour votre contribution!'), + title: const Text('Contribution'), + content: TextField( + controller: _controller, + decoration: + const InputDecoration(hintText: 'Entrez votre texte ici'), + ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: Text('Fermer'), + child: const Text('Annuler'), + ), + TextButton( + onPressed: () { + Navigator.of(context) + .pop(); // Ferme la boîte de dialogue de contribution + _showThankYouDialog( + context); // Affiche le message de remerciement + }, + child: const Text('Envoyer'), ), ], ); @@ -28,15 +66,16 @@ class MyDrawer extends StatelessWidget { Widget build(BuildContext context) { return Drawer( child: ListView( - padding: EdgeInsets.zero, children: [ DrawerHeader( - decoration: BoxDecoration(color: themeApp.colorScheme.background), - child: Text('Menu', style: titleText), + decoration: + BoxDecoration(color: Theme.of(context).colorScheme.background), + child: Text('Menu', style: titleText, textAlign: TextAlign.center), ), ListTile( title: Row( children: [ + // * "Contribuer" * // TextButton( onPressed: () { Navigator.pop(context); // Ferme le drawer @@ -47,7 +86,7 @@ class MyDrawer extends StatelessWidget { const Icon(Icons.add_circle_outline), const SizedBox(width: 25), Text("Contribuer", - style: titleText2.copyWith(color: Colors.black)), + style: titleText2.copyWith(color: Colors.black)) ], )) ], diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index 283592f..c1a0c53 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -35,7 +35,8 @@ class _HistoryState extends State { return Center( child: Column( children: [ - const SizedBox(height: 10), + // space between 2 [questions/responses] + const SizedBox(height: 0), // * QUESTION * // Row( @@ -43,47 +44,58 @@ class _HistoryState extends State { Expanded( child: Container( color: themeApp.colorScheme.primaryContainer, + // space of the question container padding: const EdgeInsets.all(10), child: Text( question, - style: titleText2, + style: normalText, textAlign: TextAlign.center, ), ), ), ], ), - const SizedBox(height: 10), // * REPONSES * // - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Container( + Padding( + padding: const EdgeInsets.all(0.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( color: themeApp.colorScheme.secondaryContainer, padding: const EdgeInsets.all(10), child: Text( reponseList[Random().nextInt(reponseList.length)], - style: titleText2, + style: normalText, textAlign: TextAlign.center, ), ), - ), - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_up, - color: themeApp.colorScheme.onPrimaryContainer, - )), - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_down, - color: themeApp.colorScheme.onPrimaryContainer, - )), - ], + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_up, + size: 18, + color: themeApp.colorScheme.onPrimaryContainer, + )), + const SizedBox( + width: + 10), // Ajoutez un espace entre les deux icônes + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_down, + size: 18, + color: themeApp.colorScheme.onPrimaryContainer, + )), + ], + ), + ], + ), ), ], ), diff --git a/lib/main page/components/TextField.dart b/lib/main page/components/TextField.dart index f1cbff1..3f21360 100644 --- a/lib/main page/components/TextField.dart +++ b/lib/main page/components/TextField.dart @@ -1,5 +1,5 @@ -import '../../main.dart'; -import '../../constants.dart'; +import 'package:chat_chuispt/constants.dart'; +import 'package:chat_chuispt/main.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -8,32 +8,56 @@ class MyTextField extends StatefulWidget { const MyTextField({Key? key}) : super(key: key); @override - State createState() => _MyTextFieldState(); + _MyTextFieldState createState() => _MyTextFieldState(); } class _MyTextFieldState extends State { - final _textController = TextEditingController(); - String userPost = ''; + TextEditingController _controller = TextEditingController(); + + void _sendMessage() { + String message = _controller.text.trim(); + if (message.isNotEmpty) { + print('Message envoyé: $message'); + var appState = context.read(); + appState.addQuestion(message); + _controller.clear(); + } + } @override Widget build(BuildContext context) { - return TextField( - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - labelStyle: normalText, - suffixIcon: IconButton( - color: themeApp.colorScheme.onPrimary, - onPressed: () { - setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); - _textController.clear(); - }); + return Container( + color: themeApp.colorScheme.primaryContainer, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _controller, + keyboardType: TextInputType.text, + style: normalText, + maxLines: 1, + decoration: InputDecoration( + hintText: 'Écrivez votre message ici...', + hintStyle: normalText, + contentPadding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(25), + borderSide: BorderSide.none, + ), + ), + onSubmitted: (value) { + _sendMessage(); }, - icon: const Icon(Icons.send))), + ), + ), + IconButton( + onPressed: _sendMessage, + icon: Icon(Icons.send, + color: themeApp.colorScheme.onPrimaryContainer), + ), + ], + ), ); } } diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index cf269e0..2d7ae8e 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -6,8 +6,8 @@ import '../main.dart'; import 'components/History.dart'; import 'components/QuestionsGrid.dart'; -import 'components/TextField.dart'; import 'components/Drawer.dart'; +import 'components/TextField.dart'; class MainPage extends StatefulWidget { const MainPage({Key? key}) : super(key: key); @@ -24,12 +24,13 @@ class _MainPageState extends State { Widget build(BuildContext context) { // * Variables : var appState = context.watch(); // état de l'application - MyTextField textField = const MyTextField(); // le champ de texte - MyDrawer myDrawer = MyDrawer(); // le drawer + MyDrawer myDrawer = const MyDrawer(); // le drawer + MyTextField textField = MyTextField(); return Consumer(builder: (context, value, child) { return Scaffold( appBar: AppBar( + scrolledUnderElevation: 10, title: Text('Chat ChuisPT', style: titleText), backgroundColor: themeApp.colorScheme.primaryContainer, elevation: 5, @@ -41,31 +42,31 @@ class _MainPageState extends State { ); })), drawer: myDrawer, - body: appState.questionsList.isEmpty - ? Container( - // ! si la liste de questions est vide - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 25), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - const Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), - ], - ), - ) - : Container( - // ! si la liste de questions n'est pas vide - color: themeApp.colorScheme.background, - child: Column(children: [ - const Flexible(child: History()), - Padding(padding: const EdgeInsets.all(20), child: textField), - ]), - ), + body: Column( + children: [ + Expanded( + child: appState.questionsList.isEmpty + ? Container( + // ! si la liste de questions est vide + color: themeApp.colorScheme.background, + child: Column( + children: [ + const SizedBox(height: 25), + Text('Exemples de questions...', style: titleText2), + const SizedBox(height: 5), + const Expanded(child: QuestionGrid()), + ], + ), + ) + : Container( + // ! si la liste de questions n'est pas vide + color: themeApp.colorScheme.background, + child: const Expanded(child: History()), + ), + ), + textField + ], + ), ); }); } diff --git a/pubspec.lock b/pubspec.lock index 2e73bc0..3513fab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -86,6 +86,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_keyboard_size: + dependency: "direct main" + description: + name: flutter_keyboard_size + sha256: "90b6a45ca79a0c756ae38bc5ed90ccaeae4d917c6141a646208c5a98e4285453" + url: "https://pub.dev" + source: hosted + version: "1.0.1" flutter_launcher_icons: dependency: "direct dev" description: @@ -282,4 +290,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.18.0 <3.0.0" - flutter: ">=1.16.0" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0c5eab7..4096b21 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,8 @@ dependencies: flutter: sdk: flutter provider: ^6.0.5 + flutter_keyboard_size: ^1.0.1 + dev_dependencies: flutter_test: @@ -22,5 +24,6 @@ flutter_icons: ios: true image_path: "assets/logo.png" + flutter: uses-material-design: true From 1de0df7b0b6145f8362f4d163cfff8de3f0230bf Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Mon, 27 Mar 2023 18:17:17 +0200 Subject: [PATCH 32/37] Getting random responses is done, and have also changed variables called questions onto responses --- lib/database_service.dart | 47 ++++-------- lib/main.dart | 3 + lib/main_page.dart | 89 +++++++++++++--------- lib/question.dart | 57 --------------- lib/response.dart | 134 ++++++++++++++++++++++++++++++++++ lib/show_features_widget.dart | 2 +- pubspec.lock | 60 +++++++-------- 7 files changed, 236 insertions(+), 156 deletions(-) delete mode 100644 lib/question.dart create mode 100644 lib/response.dart diff --git a/lib/database_service.dart b/lib/database_service.dart index 643d8fe..37700bf 100644 --- a/lib/database_service.dart +++ b/lib/database_service.dart @@ -3,41 +3,22 @@ import 'package:firebase_database/firebase_database.dart'; class DatabaseService { final DatabaseReference _database = FirebaseDatabase.instance.ref(''); - // get all the texts from the database - Stream> getTexts() { - return _database.child('questions').onValue.map((event) { - final texts = []; - - final List? data = event.snapshot.value as List?; - if (data != null) { - for (final question in data) { - final text = question['text'] as String?; - if (text != null) { - texts.add(text); - } - } - } - - return texts; - }); - } - // data format : {questions: {id: {text: text, blue_thumb: blueThumb, red_thumb: redThumb}}, ...} // get all the data from the database Stream>> getData() { - return _database.child('questions').onValue.map((event) { + return _database.child('responses').onValue.map((event) { final data = >[]; - final Map? questions = + final Map? responses = event.snapshot.value as Map?; - if (questions != null) { - questions.forEach((key, value) { - final Map? questionMap = + if (responses != null) { + responses.forEach((key, value) { + final Map? responseMap = value as Map?; - final text = questionMap?['text'] as String?; - final blueThumb = questionMap?['blue_thumb'] as int?; - final redThumb = questionMap?['red_thumb'] as int?; + final text = responseMap?['text'] as String?; + final blueThumb = responseMap?['blue_thumb'] as int?; + final redThumb = responseMap?['red_thumb'] as int?; if (text != null && blueThumb != null && redThumb != null) { data.add({ @@ -55,9 +36,9 @@ class DatabaseService { } // add a new answer to the database, with his id, text, blue thumb and red thumb - Future addQuestion(String text, int blueThumb, int redThumb) { - final int questionId = DateTime.now().millisecondsSinceEpoch; - return _database.child('questions/$questionId').set({ + Future addResponse(String text, int blueThumb, int redThumb) { + final int responseId = DateTime.now().millisecondsSinceEpoch; + return _database.child('questions/$responseId').set({ 'text': text, 'blue_thumb': blueThumb, 'red_thumb': redThumb, @@ -66,20 +47,20 @@ class DatabaseService { // update the blue thumb of a question Future updateBlueThumb(String id, int blueThumb) { - return _database.child('questions').child(id.toString()).update({ + return _database.child('responses').child(id.toString()).update({ 'blue_thumb': blueThumb, }); } // update the red thumb of a question Future updateRedThumb(String id, int redThumb) { - return _database.child('questions').child(id.toString()).update({ + return _database.child('responses').child(id.toString()).update({ 'red_thumb': redThumb, }); } // delete a question Future deleteQuestion(String id) { - return _database.child('questions').child(id.toString()).remove(); + return _database.child('responses').child(id.toString()).remove(); } } diff --git a/lib/main.dart b/lib/main.dart index 9ae840c..e020fec 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,9 @@ import 'firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + if (kDebugMode) { + print("Firebase initializing..."); + } try { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); diff --git a/lib/main_page.dart b/lib/main_page.dart index e2dd126..84b436e 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -1,4 +1,6 @@ import 'dart:math'; +import 'package:chat_chuispt/database_service.dart'; +import 'package:chat_chuispt/response.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -244,48 +246,65 @@ class History extends StatefulWidget { class _HistoryState extends State { final _key = GlobalKey(); - List reponseList = ['oui', 'non', 'peut-être', 'je ne sais pas']; + final DatabaseService _databaseService = DatabaseService(); @override Widget build(BuildContext context) { final appState = context.watch(); appState.historyKey = _key; - return AnimatedList( - key: _key, - reverse: true, - initialItemCount: appState.questionsList.length, - itemBuilder: (context, index, animation) { - final question = appState.questionsList[index]; - return SizeTransition( - sizeFactor: animation, - child: Center( - child: Column( - children: [ - const SizedBox(height: 10), - Container( - // * QUESTION * // - color: themeApp.colorScheme.primaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - question, - style: titleText2, - textAlign: TextAlign.center, - )), - const SizedBox(height: 10), - Container( - // * REPONSES * // - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: titleText2, - textAlign: TextAlign.center, - ), + return StreamBuilder>>( + stream: _databaseService.getData(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return const Center(child: Text('Une erreur est survenue')); + } + + final reponseList = snapshot.data ?? []; + final LocalResponseList localResponseList = LocalResponseList(); + localResponseList.addResponseListFromMap(reponseList); + + return AnimatedList( + key: _key, + reverse: true, + initialItemCount: appState.questionsList.length, + itemBuilder: (context, index, animation) { + final question = appState.questionsList[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( + child: Column( + children: [ + const SizedBox(height: 10), + Container( + // * QUESTION * // + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + )), + const SizedBox(height: 10), + Container( + // * REPONSES * // + color: themeApp.colorScheme.secondaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + localResponseList.getRandomResponseWithWeights().text, + style: titleText2, + textAlign: TextAlign.center, + ), + ), + ], ), - ], - ), - ), + ), + ); + }, ); }, ); diff --git a/lib/question.dart b/lib/question.dart deleted file mode 100644 index dfbd7e0..0000000 --- a/lib/question.dart +++ /dev/null @@ -1,57 +0,0 @@ -class LocalQuestion { - int id = 0; - String text = ""; - int blueThumb = 0; - int redThumb = 0; - int usage = 0; - double repetitionScore = 1.0; - double globalScore = 0.0; - - LocalQuestion( - {required this.id, - required this.text, - required this.blueThumb, - required this.redThumb}) { - updateGlobalScore(); - } - - String getText() { - return text; - } - - void increaseBlueThumb() { - blueThumb++; - } - - void increaseRedThumb() { - redThumb++; - } - - void increaseUsage() { - usage++; - } - - void addUsage(int elementNumber) { - usage++; - repetitionScore /= elementNumber; - updateGlobalScore(); - } - - void addNonUsage(int elementNumber) { - repetitionScore *= (1 + 1 / elementNumber); - updateGlobalScore(); - } - - void updateGlobalScore() { - globalScore = blueThumb / redThumb * repetitionScore; - } - - static LocalQuestion fromJson(Map? json) { - return LocalQuestion( - id: json?['id'] ?? 0, - text: json?['text'] ?? "", - blueThumb: json?['blue_thumb'] ?? 0, - redThumb: json?['red_thumb'] ?? 0, - ); - } -} diff --git a/lib/response.dart b/lib/response.dart new file mode 100644 index 0000000..ca8b353 --- /dev/null +++ b/lib/response.dart @@ -0,0 +1,134 @@ +import 'dart:math'; + +import 'package:flutter/foundation.dart'; + +class LocalResponse { + String id = ""; + String text = ""; + int blueThumb = 0; + int redThumb = 0; + int usage = 0; + double repetitionScore = 1.0; + double globalScore = 1.0; + + LocalResponse( + {required this.id, + required this.text, + required this.blueThumb, + required this.redThumb}) { + updateGlobalScore(); + } + + @override + String toString() { + return "id: $id\t" + "text: $text\t" + "blueThumb: $blueThumb\t" + "redThumb: $redThumb\t" + "usage: $usage\t" + "repetitionScore: $repetitionScore\t" + "globalScore: $globalScore\t"; + } + + String getText() { + return text; + } + + void increaseBlueThumb() { + blueThumb++; + } + + void increaseRedThumb() { + redThumb++; + } + + void increaseUsage() { + usage++; + } + + void addUsage(int elementNumber) { + usage++; + repetitionScore /= elementNumber; + updateGlobalScore(); + } + + void addNonUsage(int elementNumber) { + repetitionScore *= (1 + 1 / elementNumber); + updateGlobalScore(); + } + + void updateGlobalScore() { + if (redThumb == 0) { + globalScore = blueThumb * repetitionScore; + } else if (blueThumb == 0) { + globalScore = 1 / redThumb * repetitionScore; + } else if (blueThumb == redThumb) { + globalScore = 1 * repetitionScore; + } else { + globalScore = blueThumb / redThumb * repetitionScore; + } + } +} + +class LocalResponseList { + static List listResponse = []; + + LocalResponseList(); + + @override + String toString() { + String result = ""; + for (var response in listResponse) { + result += "$response\n"; + } + return result; + } + + void addResponse(LocalResponse response) { + listResponse.add(response); + } + + void addResponseList(List responseList) { + listResponse.addAll(responseList); + } + + void addResponseListFromMap(List> responseList) { + for (var response in responseList) { + listResponse.add(LocalResponse( + id: response['id'], + text: response['text'], + blueThumb: response['blue_thumb'], + redThumb: response['red_thumb'])); + } + } + + LocalResponse getRandomResponseWithWeights() { + if (listResponse.isEmpty) { + throw Exception("La liste est vide, aucune réponse à renvoyer."); + } + + double totalWeight = + listResponse.fold(0, (sum, item) => sum + item.globalScore); + double randomWeight = Random().nextDouble() * totalWeight; + double cumulativeWeight = 0; + + for (var response in listResponse) { + cumulativeWeight += response.globalScore; + if (randomWeight <= cumulativeWeight) { + response.addUsage(listResponse.length); + for (var otherResponse in listResponse) { + if (otherResponse != response) { + otherResponse.addNonUsage(listResponse.length); + } + } + if (kDebugMode) { + print(response); + } + return response; + } + } + + // This should never be reached, but return the last response in the list just in case + return listResponse.last; + } +} diff --git a/lib/show_features_widget.dart b/lib/show_features_widget.dart index 5adbcb9..a19f116 100644 --- a/lib/show_features_widget.dart +++ b/lib/show_features_widget.dart @@ -110,7 +110,7 @@ class _AddQuestionWidgetState extends State { ), ElevatedButton( onPressed: () { - DatabaseService().addQuestion( + DatabaseService().addResponse( _controller.text, 0, 0, diff --git a/pubspec.lock b/pubspec.lock index aee3cbf..8f02292 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: @@ -268,10 +268,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8 url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.14" material_color_utilities: dependency: transitive description: @@ -284,10 +284,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" mockito: dependency: "direct main" description: @@ -296,14 +296,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.3.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" nested: dependency: transitive description: @@ -312,14 +304,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" plugin_platform_interface: dependency: transitive description: @@ -328,14 +328,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" - url: "https://pub.dev" - source: hosted - version: "2.1.3" provider: dependency: "direct main" description: @@ -344,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -401,10 +401,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.4.18" typed_data: dependency: transitive description: @@ -438,5 +438,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.0 <3.0.0" - flutter: ">=1.16.0" + dart: ">=2.19.0 <4.0.0" + flutter: ">=1.20.0" From 847c3ed9488c9b318a88f614ee489fb4eb3bbbf3 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Mon, 27 Mar 2023 18:59:47 +0200 Subject: [PATCH 33/37] Just added some few comments --- lib/database_service.dart | 33 ++++----- lib/main.dart | 2 + lib/main_page.dart | 28 ++++---- lib/show_features_widget.dart | 124 ---------------------------------- 4 files changed, 32 insertions(+), 155 deletions(-) delete mode 100644 lib/show_features_widget.dart diff --git a/lib/database_service.dart b/lib/database_service.dart index 37700bf..ed5cd17 100644 --- a/lib/database_service.dart +++ b/lib/database_service.dart @@ -1,13 +1,14 @@ import 'package:firebase_database/firebase_database.dart'; class DatabaseService { - final DatabaseReference _database = FirebaseDatabase.instance.ref(''); + final DatabaseReference _databaseReference = + FirebaseDatabase.instance.ref(''); - // data format : {questions: {id: {text: text, blue_thumb: blueThumb, red_thumb: redThumb}}, ...} - // get all the data from the database + // data format : {responses: {id: {text: text, blue_thumb: blueThumb, red_thumb: redThumb}}, ...} + // Fetches all the data from the database Stream>> getData() { - return _database.child('responses').onValue.map((event) { - final data = >[]; + return _databaseReference.child('responses').onValue.map((event) { + final List> data = []; final Map? responses = event.snapshot.value as Map?; @@ -16,9 +17,9 @@ class DatabaseService { final Map? responseMap = value as Map?; - final text = responseMap?['text'] as String?; - final blueThumb = responseMap?['blue_thumb'] as int?; - final redThumb = responseMap?['red_thumb'] as int?; + final String? text = responseMap?['text'] as String?; + final int? blueThumb = responseMap?['blue_thumb'] as int?; + final int? redThumb = responseMap?['red_thumb'] as int?; if (text != null && blueThumb != null && redThumb != null) { data.add({ @@ -35,32 +36,32 @@ class DatabaseService { }); } - // add a new answer to the database, with his id, text, blue thumb and red thumb + // add a new answer to the database, with its id, text, blue thumb and red thumb Future addResponse(String text, int blueThumb, int redThumb) { final int responseId = DateTime.now().millisecondsSinceEpoch; - return _database.child('questions/$responseId').set({ + return _databaseReference.child('responses/$responseId').set({ 'text': text, 'blue_thumb': blueThumb, 'red_thumb': redThumb, }); } - // update the blue thumb of a question + // Updates the blue thumb of a question Future updateBlueThumb(String id, int blueThumb) { - return _database.child('responses').child(id.toString()).update({ + return _databaseReference.child('responses').child(id.toString()).update({ 'blue_thumb': blueThumb, }); } - // update the red thumb of a question + // Updates the red thumb of a question Future updateRedThumb(String id, int redThumb) { - return _database.child('responses').child(id.toString()).update({ + return _databaseReference.child('responses').child(id.toString()).update({ 'red_thumb': redThumb, }); } // delete a question - Future deleteQuestion(String id) { - return _database.child('responses').child(id.toString()).remove(); + Future deleteResponse(String id) { + return _databaseReference.child('responses').child(id.toString()).remove(); } } diff --git a/lib/main.dart b/lib/main.dart index e020fec..8f4402e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,6 +45,7 @@ class MainAppState extends ChangeNotifier { List responsesList = []; var historyKey = GlobalKey(); + // Adds a question to the list and updates the UI void addQuestion(String question) { if (question != '') { questionsList.insert(0, question); @@ -54,6 +55,7 @@ class MainAppState extends ChangeNotifier { } } + // Clears the question list void clearQuestionList() { questionsList.clear(); } diff --git a/lib/main_page.dart b/lib/main_page.dart index 84b436e..dae3a1f 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -16,15 +16,14 @@ class MainPage extends StatefulWidget { class _MainPageState extends State { // store the user's question - String userPost = ''; + String userQuestion = ''; @override Widget build(BuildContext context) { var appState = context.watch(); MyTextField textField = const MyTextField(); - // ! LISTE VIDE ! // - + // Empty list case return Consumer(builder: (context, value, child) { return Scaffold( body: appState.questionsList.isEmpty @@ -72,8 +71,7 @@ class _MainPageState extends State { ) : - // ! LISTE NON VIDE ! // - + // Non-empty list case Row( children: [ Container( @@ -125,7 +123,7 @@ class MyTextField extends StatefulWidget { class _MyTextFieldState extends State { final _textController = TextEditingController(); - String userPost = ''; + String userQuestion = ''; @override Widget build(BuildContext context) { @@ -141,8 +139,8 @@ class _MyTextFieldState extends State { color: themeApp.colorScheme.onPrimary, onPressed: () { setState(() { - userPost = _textController.text; - context.read().addQuestion(userPost); + userQuestion = _textController.text; + context.read().addQuestion(userQuestion); _textController.clear(); }); }, @@ -175,8 +173,8 @@ class _QuestionGridState extends State { Widget build(BuildContext context) { List questions = []; - // * Génération du tableau aléatoire de questions * // - String userPost = ""; + // Generate a random array of questions + String userQuestion = ""; for (int i = 1; i <= 4; i++) { questions.add( Container( @@ -184,8 +182,8 @@ class _QuestionGridState extends State { child: TextButton( onPressed: () { setState(() { - userPost = exemples[Random().nextInt(exemples.length)]; - context.read().addQuestion(userPost); + userQuestion = exemples[Random().nextInt(exemples.length)]; + context.read().addQuestion(userQuestion); }); }, child: Text( @@ -196,7 +194,7 @@ class _QuestionGridState extends State { ); } - // ! AFFICHAGE ! // + // Display return Column( children: [ Row( @@ -261,7 +259,7 @@ class _HistoryState extends State { } if (snapshot.hasError) { - return const Center(child: Text('Une erreur est survenue')); + return const Center(child: Text('An error has occurred')); } final reponseList = snapshot.data ?? []; @@ -291,7 +289,7 @@ class _HistoryState extends State { )), const SizedBox(height: 10), Container( - // * REPONSES * // + // * RESPONSES * // color: themeApp.colorScheme.secondaryContainer, padding: const EdgeInsets.all(10), child: Text( diff --git a/lib/show_features_widget.dart b/lib/show_features_widget.dart deleted file mode 100644 index a19f116..0000000 --- a/lib/show_features_widget.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:chat_chuispt/database_service.dart'; - -class TextListWidget extends StatelessWidget { - final Stream> stream; - - const TextListWidget({Key? key, required this.stream}) : super(key: key); - - @override - Widget build(BuildContext context) { - return StreamBuilder>( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - final texts = snapshot.data!; - return ListView.builder( - itemCount: texts.length, - itemBuilder: (context, index) { - final text = texts[index]; - return ListTile( - title: Text(text), - ); - }, - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ); - } -} - -class DataListWidget extends StatelessWidget { - final Stream>> stream; - - const DataListWidget({Key? key, required this.stream}) : super(key: key); - - @override - Widget build(BuildContext context) { - return StreamBuilder>>( - stream: stream, - builder: (context, snapshot) { - if (snapshot.hasData) { - final data = snapshot.data!; - return ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) { - final question = data[index]; - final id = question['id'] as String; - final text = question['text'] as String; - final blueThumb = question['blue_thumb'] as int; - final redThumb = question['red_thumb'] as int; - - return ListTile( - title: Text(text), - subtitle: Text('$blueThumb - $redThumb'), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.thumb_up), - onPressed: () { - DatabaseService().updateBlueThumb(id, blueThumb + 1); - }, - ), - IconButton( - icon: const Icon(Icons.thumb_down), - onPressed: () { - DatabaseService().updateRedThumb(id, redThumb + 1); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - DatabaseService().deleteQuestion(id); - }, - ), - ], - ), - ); - }, - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ); - } -} - -// The goal of this widget is to allow the user to add a new question to the database -// The user can add a question by typing it in the text field and clicking on the button -// The question is added to the database with the id, text, blue thumb and red thumb -class AddQuestionWidget extends StatefulWidget { - const AddQuestionWidget({Key? key}) : super(key: key); - - @override - _AddQuestionWidgetState createState() => _AddQuestionWidgetState(); -} - -class _AddQuestionWidgetState extends State { - final TextEditingController _controller = TextEditingController(); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - TextField( - controller: _controller, - ), - ElevatedButton( - onPressed: () { - DatabaseService().addResponse( - _controller.text, - 0, - 0, - ); - }, - child: const Text('Add question'), - ), - ], - ); - } -} From 93c66c31ed19a1380840ede69ef4f3e10572c63c Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 27 Mar 2023 20:35:28 +0200 Subject: [PATCH 34/37] Front update --- lib/constants.dart | 18 ++-- lib/main page/components/Drawer.dart | 40 ++++++--- lib/main page/components/History.dart | 84 ++++++++++--------- lib/main page/components/QuestionsGrid.dart | 47 ++++++----- lib/main page/components/TextField.dart | 13 ++- lib/main page/main_page.dart | 5 +- pubspec.lock | 92 ++++++++++----------- 7 files changed, 168 insertions(+), 131 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index 325928b..25100c0 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -22,22 +22,22 @@ ThemeData themeApp = ThemeData( colorScheme: ColorScheme( // ? PRIMAIRES ? // - primary: Colors.blueGrey.shade900, - secondary: Colors.blueGrey.shade500, - primaryContainer: Colors.blueGrey.shade700, - secondaryContainer: Colors.blueGrey.shade400, + primary: const Color(0xFF444654), // responses + secondary: const Color(0xFF343541), // questions, text Field and appBar + primaryContainer: const Color(0xFF202123), // drawer + secondaryContainer: const Color(0xFF3E3F4B), //Question grid error: Colors.red, // ? SURFACE ? // surface: Colors.blueGrey.shade900, - background: Colors.blueGrey.shade400, + background: const Color(0xFF444654), // like responses // ? ON ? // onPrimary: Colors.white, onPrimaryContainer: Colors.white, onSecondary: Colors.white, - onSurface: Colors.blueGrey.shade400, - onBackground: Colors.blueGrey.shade400, - onError: Colors.black, - brightness: Brightness.light), + onSurface: Colors.white, + onBackground: Colors.white, + onError: Colors.white, + brightness: Brightness.dark), ); diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart index 05c24ec..b8b99f8 100644 --- a/lib/main page/components/Drawer.dart +++ b/lib/main page/components/Drawer.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import '../../main.dart'; import '../../constants.dart'; @@ -45,7 +46,7 @@ class MyDrawer extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); }, - child: const Text('Annuler'), + child: Text('Annuler', style: normalText), ), TextButton( onPressed: () { @@ -54,7 +55,7 @@ class MyDrawer extends StatelessWidget { _showThankYouDialog( context); // Affiche le message de remerciement }, - child: const Text('Envoyer'), + child: Text('Envoyer', style: normalText), ), ], ); @@ -64,18 +65,18 @@ class MyDrawer extends StatelessWidget { @override Widget build(BuildContext context) { + var appState = context.watch(); // état de l'application + return Drawer( + backgroundColor: themeApp.colorScheme.primaryContainer.withOpacity(1), child: ListView( children: [ - DrawerHeader( - decoration: - BoxDecoration(color: Theme.of(context).colorScheme.background), - child: Text('Menu', style: titleText, textAlign: TextAlign.center), - ), ListTile( - title: Row( + title: Column( children: [ - // * "Contribuer" * // + const SizedBox(height: 35), + + // * "Contribuer" button * // TextButton( onPressed: () { Navigator.pop(context); // Ferme le drawer @@ -83,10 +84,25 @@ class MyDrawer extends StatelessWidget { }, child: Row( children: [ - const Icon(Icons.add_circle_outline), + Icon(Icons.add_circle_outline, + color: themeApp.colorScheme.onPrimaryContainer), + const SizedBox(width: 25), + Text("Contribuer", style: titleText2), + ], + )), + const SizedBox(height: 15), + + // * "Reinitialisation" button * // + TextButton( + onPressed: () { + appState.clearQuestionList(); + }, + child: Row( + children: [ + Icon(Icons.restore, + color: themeApp.colorScheme.onPrimaryContainer), const SizedBox(width: 25), - Text("Contribuer", - style: titleText2.copyWith(color: Colors.black)) + Text("Réinitialisation", style: titleText2), ], )) ], diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index c1a0c53..ddb84be 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -1,3 +1,5 @@ +import 'dart:ffi'; + import '../../main.dart'; import '../../constants.dart'; @@ -26,6 +28,7 @@ class _HistoryState extends State { Widget build(BuildContext context) { final appState = context.watch(); appState.historyKey = _key; + double largeurEcran = MediaQuery.of(context).size.width; return ListView.builder( reverse: true, @@ -43,7 +46,7 @@ class _HistoryState extends State { children: [ Expanded( child: Container( - color: themeApp.colorScheme.primaryContainer, + color: themeApp.colorScheme.secondary, // space of the question container padding: const EdgeInsets.all(10), child: Text( @@ -57,45 +60,50 @@ class _HistoryState extends State { ), // * REPONSES * // - Padding( - padding: const EdgeInsets.all(0.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: normalText, - textAlign: TextAlign.center, + Row( + children: [ + Expanded( + child: Container( + color: themeApp.colorScheme.primary, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 10), + // * Response + Text( + reponseList[Random().nextInt(reponseList.length)], + style: normalText, + textAlign: TextAlign.center, + ), + + // * Thumbs + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_up, + size: 18, + color: + themeApp.colorScheme.onPrimaryContainer, + )), + IconButton( + onPressed: () {}, + icon: Icon( + Icons.thumb_down, + size: 18, + color: + themeApp.colorScheme.onPrimaryContainer, + )), + ], + ), + ], ), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_up, - size: 18, - color: themeApp.colorScheme.onPrimaryContainer, - )), - const SizedBox( - width: - 10), // Ajoutez un espace entre les deux icônes - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_down, - size: 18, - color: themeApp.colorScheme.onPrimaryContainer, - )), - ], - ), - ], - ), + ), + ], ), ], ), diff --git a/lib/main page/components/QuestionsGrid.dart b/lib/main page/components/QuestionsGrid.dart index d8dc1bf..17de206 100644 --- a/lib/main page/components/QuestionsGrid.dart +++ b/lib/main page/components/QuestionsGrid.dart @@ -34,7 +34,9 @@ class _QuestionGridState extends State { for (int i = 1; i <= 4; i++) { questions.add( Container( - color: Colors.grey[300], + decoration: BoxDecoration( + color: themeApp.colorScheme.secondaryContainer.withOpacity(1), + borderRadius: BorderRadius.circular(15)), child: TextButton( onPressed: () { setState(() { @@ -42,9 +44,13 @@ class _QuestionGridState extends State { context.read().addQuestion(userPost); }); }, - child: Text( - exemples[Random().nextInt(exemples.length)], - textAlign: TextAlign.center, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + style: normalText, + exemples[Random().nextInt(exemples.length)], + textAlign: TextAlign.center, + ), ), )), ); @@ -53,35 +59,32 @@ class _QuestionGridState extends State { // ! AFFICHAGE ! // return Column( children: [ + SizedBox(height: 60), Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.help_center_outlined, - color: themeApp.colorScheme.onPrimary, - ), + Center( + child: Icon( + Icons.help_center_outlined, + color: themeApp.colorScheme.onPrimary, ), ), - Padding( - padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.question_answer, - color: themeApp.colorScheme.onPrimary, - ), + SizedBox(width: 155), + Center( + child: Icon( + Icons.question_answer, + color: themeApp.colorScheme.onPrimary, ), ), ], ), + SizedBox(height: 30), Expanded( child: GridView.count( - padding: const EdgeInsets.only(right: 20, left: 20), + padding: const EdgeInsets.symmetric(horizontal: 20), primary: false, - crossAxisSpacing: 10, - mainAxisSpacing: 10, + crossAxisSpacing: 20, + mainAxisSpacing: 20, crossAxisCount: 2, children: questions, ), diff --git a/lib/main page/components/TextField.dart b/lib/main page/components/TextField.dart index 3f21360..a33953b 100644 --- a/lib/main page/components/TextField.dart +++ b/lib/main page/components/TextField.dart @@ -27,7 +27,18 @@ class _MyTextFieldState extends State { @override Widget build(BuildContext context) { return Container( - color: themeApp.colorScheme.primaryContainer, + decoration: BoxDecoration( + color: themeApp.colorScheme.secondary, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset( + 0, 3), // position de l'ombre par rapport au conteneur + ), + ], + ), child: Row( children: [ Expanded( diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index 2d7ae8e..e36029d 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -25,14 +25,15 @@ class _MainPageState extends State { // * Variables : var appState = context.watch(); // état de l'application MyDrawer myDrawer = const MyDrawer(); // le drawer - MyTextField textField = MyTextField(); + MyTextField textField = const MyTextField(); return Consumer(builder: (context, value, child) { return Scaffold( appBar: AppBar( scrolledUnderElevation: 10, + toolbarHeight: 75, title: Text('Chat ChuisPT', style: titleText), - backgroundColor: themeApp.colorScheme.primaryContainer, + backgroundColor: themeApp.colorScheme.secondary, elevation: 5, leading: Builder(builder: (context) { return IconButton( diff --git a/pubspec.lock b/pubspec.lock index 4917472..36f5a4f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,14 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d - url: "https://pub.dev" - source: hosted - version: "3.3.6" _fe_analyzer_shared: dependency: transitive description: @@ -33,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.7.1" + archive: + dependency: transitive + description: + name: archive + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" + source: hosted + version: "3.3.6" args: dependency: transitive description: @@ -129,7 +129,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - dart_style: dependency: transitive description: @@ -252,15 +251,6 @@ packages: description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" - url: "https://pub.dev" - source: hosted - version: "3.3.0" - flutter_web_plugins: dependency: transitive description: flutter @@ -274,6 +264,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + image: + dependency: transitive + description: + name: image + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" + source: hosted + version: "3.3.0" js: dependency: transitive description: @@ -330,14 +328,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.3.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" nested: dependency: transitive description: @@ -346,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -362,14 +360,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c - url: "https://pub.dev" - source: hosted - version: "3.7.2" plugin_platform_interface: dependency: transitive description: @@ -378,14 +368,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - pub_semver: + pointycastle: dependency: transitive description: - name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + name: pointycastle + sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "3.7.2" provider: dependency: "direct main" description: @@ -394,6 +384,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -471,14 +469,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - xml: - dependency: transitive - description: - name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" - url: "https://pub.dev" - source: hosted - version: "6.2.2" watcher: dependency: transitive description: @@ -487,6 +477,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" yaml: dependency: transitive description: @@ -496,5 +494,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.19.0 <3.0.0" flutter: ">=2.0.0" From 6a719589371166c7e9a6527f679db6d8c5caa4b3 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Mon, 27 Mar 2023 21:31:27 +0200 Subject: [PATCH 35/37] Sharing out files in folders --- lib/main page/components/Drawer.dart | 1 - lib/main page/components/History.dart | 107 +++---- lib/main page/components/QuestionsGrid.dart | 12 +- lib/main page/components/TextField.dart | 63 ++-- lib/main page/main_page.dart | 113 ++++--- lib/main_page.dart | 310 -------------------- pubspec.lock | 68 +++-- 7 files changed, 179 insertions(+), 495 deletions(-) delete mode 100644 lib/main_page.dart diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart index 05c24ec..a4f562a 100644 --- a/lib/main page/components/Drawer.dart +++ b/lib/main page/components/Drawer.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import '../../main.dart'; import '../../constants.dart'; class MyDrawer extends StatelessWidget { diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index c1a0c53..d20b9ad 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -1,3 +1,4 @@ +import '../../database_service.dart'; import '../../main.dart'; import '../../constants.dart'; @@ -5,6 +6,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../response.dart'; + class History extends StatefulWidget { const History({Key? key}) : super(key: key); @@ -14,91 +17,65 @@ class History extends StatefulWidget { class _HistoryState extends State { final _key = GlobalKey(); - List reponseList = [ - 'oui', - 'non', - 'peut-être', - 'je ne sais pas', - 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - ]; + final DatabaseService _databaseService = DatabaseService(); @override Widget build(BuildContext context) { final appState = context.watch(); appState.historyKey = _key; - return ListView.builder( - reverse: true, - itemCount: appState.questionsList.length, - itemBuilder: (context, index) { - final question = appState.questionsList[index]; - return Center( - child: Column( - children: [ - // space between 2 [questions/responses] - const SizedBox(height: 0), + return StreamBuilder>>( + stream: _databaseService.getData(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } - // * QUESTION * // - Row( - children: [ - Expanded( - child: Container( - color: themeApp.colorScheme.primaryContainer, - // space of the question container - padding: const EdgeInsets.all(10), - child: Text( - question, - style: normalText, - textAlign: TextAlign.center, - ), - ), - ), - ], - ), + if (snapshot.hasError) { + return const Center(child: Text('An error has occurred')); + } + + final reponseList = snapshot.data ?? []; + final LocalResponseList localResponseList = LocalResponseList(); + localResponseList.addResponseListFromMap(reponseList); - // * REPONSES * // - Padding( - padding: const EdgeInsets.all(0.0), + return AnimatedList( + key: _key, + reverse: true, + initialItemCount: appState.questionsList.length, + itemBuilder: (context, index, animation) { + final question = appState.questionsList[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ + const SizedBox(height: 10), Container( + // * QUESTION * // + color: themeApp.colorScheme.primaryContainer, + padding: const EdgeInsets.all(10), + child: Text( + question, + style: titleText2, + textAlign: TextAlign.center, + )), + const SizedBox(height: 10), + Container( + // * RESPONSES * // color: themeApp.colorScheme.secondaryContainer, padding: const EdgeInsets.all(10), child: Text( - reponseList[Random().nextInt(reponseList.length)], - style: normalText, + localResponseList.getRandomResponseWithWeights().text, + style: titleText2, textAlign: TextAlign.center, ), ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_up, - size: 18, - color: themeApp.colorScheme.onPrimaryContainer, - )), - const SizedBox( - width: - 10), // Ajoutez un espace entre les deux icônes - IconButton( - onPressed: () {}, - icon: Icon( - Icons.thumb_down, - size: 18, - color: themeApp.colorScheme.onPrimaryContainer, - )), - ], - ), ], ), ), - ], - ), + ); + }, ); }, ); diff --git a/lib/main page/components/QuestionsGrid.dart b/lib/main page/components/QuestionsGrid.dart index d8dc1bf..c556c54 100644 --- a/lib/main page/components/QuestionsGrid.dart +++ b/lib/main page/components/QuestionsGrid.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class QuestionGrid extends StatefulWidget { - const QuestionGrid({Key? key}) : super(key: key); + QuestionGrid({Key? key}) : super(key: key); @override State createState() => _QuestionGridState(); @@ -29,8 +29,8 @@ class _QuestionGridState extends State { Widget build(BuildContext context) { List questions = []; - // * Génération du tableau aléatoire de questions * // - String userPost = ""; + // Generate a random array of questions + String userQuestion = ""; for (int i = 1; i <= 4; i++) { questions.add( Container( @@ -38,8 +38,8 @@ class _QuestionGridState extends State { child: TextButton( onPressed: () { setState(() { - userPost = exemples[Random().nextInt(exemples.length)]; - context.read().addQuestion(userPost); + userQuestion = exemples[Random().nextInt(exemples.length)]; + context.read().addQuestion(userQuestion); }); }, child: Text( @@ -50,7 +50,7 @@ class _QuestionGridState extends State { ); } - // ! AFFICHAGE ! // + // Display return Column( children: [ Row( diff --git a/lib/main page/components/TextField.dart b/lib/main page/components/TextField.dart index 3f21360..0c5ffbc 100644 --- a/lib/main page/components/TextField.dart +++ b/lib/main page/components/TextField.dart @@ -8,56 +8,33 @@ class MyTextField extends StatefulWidget { const MyTextField({Key? key}) : super(key: key); @override - _MyTextFieldState createState() => _MyTextFieldState(); + State createState() => _MyTextFieldState(); } class _MyTextFieldState extends State { - TextEditingController _controller = TextEditingController(); - - void _sendMessage() { - String message = _controller.text.trim(); - if (message.isNotEmpty) { - print('Message envoyé: $message'); - var appState = context.read(); - appState.addQuestion(message); - _controller.clear(); - } - } + final _textController = TextEditingController(); + String userQuestion = ''; @override Widget build(BuildContext context) { - return Container( - color: themeApp.colorScheme.primaryContainer, - child: Row( - children: [ - Expanded( - child: TextField( - controller: _controller, - keyboardType: TextInputType.text, - style: normalText, - maxLines: 1, - decoration: InputDecoration( - hintText: 'Écrivez votre message ici...', - hintStyle: normalText, - contentPadding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(25), - borderSide: BorderSide.none, - ), - ), - onSubmitted: (value) { - _sendMessage(); + return TextField( + autocorrect: true, + controller: _textController, + style: normalText, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'Posez votre question ici...', + labelStyle: normalText, + suffixIcon: IconButton( + color: themeApp.colorScheme.onPrimary, + onPressed: () { + setState(() { + userQuestion = _textController.text; + context.read().addQuestion(userQuestion); + _textController.clear(); + }); }, - ), - ), - IconButton( - onPressed: _sendMessage, - icon: Icon(Icons.send, - color: themeApp.colorScheme.onPrimaryContainer), - ), - ], - ), + icon: const Icon(Icons.send))), ); } } diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index 2d7ae8e..49929e8 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -6,7 +6,6 @@ import '../main.dart'; import 'components/History.dart'; import 'components/QuestionsGrid.dart'; -import 'components/Drawer.dart'; import 'components/TextField.dart'; class MainPage extends StatefulWidget { @@ -18,55 +17,99 @@ class MainPage extends StatefulWidget { class _MainPageState extends State { // store the user's question - String userPost = ''; + String userQuestion = ''; @override Widget build(BuildContext context) { - // * Variables : - var appState = context.watch(); // état de l'application - MyDrawer myDrawer = const MyDrawer(); // le drawer - MyTextField textField = MyTextField(); + var appState = context.watch(); + MyTextField textField = const MyTextField(); + // Empty list case return Consumer(builder: (context, value, child) { return Scaffold( - appBar: AppBar( - scrolledUnderElevation: 10, - title: Text('Chat ChuisPT', style: titleText), - backgroundColor: themeApp.colorScheme.primaryContainer, - elevation: 5, - leading: Builder(builder: (context) { - return IconButton( - icon: const Icon(Icons.menu), - onPressed: () => - Scaffold.of(context).openDrawer(), // open the drawer), - ); - })), - drawer: myDrawer, - body: Column( - children: [ - Expanded( - child: appState.questionsList.isEmpty - ? Container( - // ! si la liste de questions est vide + body: appState.questionsList.isEmpty + ? Row( + children: [ + Container( + color: themeApp.colorScheme.primaryContainer, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context + .read() + .clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )), + Expanded( + child: Container( color: themeApp.colorScheme.background, child: Column( children: [ - const SizedBox(height: 25), + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 5), Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), - const Expanded(child: QuestionGrid()), + Expanded(child: QuestionGrid()), + Padding( + padding: const EdgeInsets.only( + bottom: 25, top: 25, right: 15, left: 15), + child: textField), ], ), - ) - : Container( - // ! si la liste de questions n'est pas vide + ), + ), + ], + ) + : + + // Non-empty list case + Row( + children: [ + Container( + color: themeApp.colorScheme.primaryContainer, + width: 75, + child: Column( + children: [ + const Padding(padding: EdgeInsets.only(top: 75)), + IconButton( + iconSize: 35, + color: themeApp.colorScheme.onPrimaryContainer, + onPressed: () { + setState(() { + context + .read() + .clearQuestionList(); + }); + }, + icon: const Icon(Icons.refresh)) + ], + )), + Expanded( + child: Container( color: themeApp.colorScheme.background, - child: const Expanded(child: History()), + child: Column(children: [ + const SizedBox(height: 75), + Text('Chat ChuisPT', style: titleText), + const SizedBox(height: 25), + const Flexible(child: History()), + Padding( + padding: const EdgeInsets.all(20), + child: textField), + ]), ), - ), - textField - ], - ), + ), + ], + ), ); }); } diff --git a/lib/main_page.dart b/lib/main_page.dart deleted file mode 100644 index dae3a1f..0000000 --- a/lib/main_page.dart +++ /dev/null @@ -1,310 +0,0 @@ -import 'dart:math'; -import 'package:chat_chuispt/database_service.dart'; -import 'package:chat_chuispt/response.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'textstyle.dart'; -import 'main.dart'; - -class MainPage extends StatefulWidget { - const MainPage({Key? key}) : super(key: key); - - @override - State createState() => _MainPageState(); -} - -class _MainPageState extends State { - // store the user's question - String userQuestion = ''; - - @override - Widget build(BuildContext context) { - var appState = context.watch(); - MyTextField textField = const MyTextField(); - - // Empty list case - return Consumer(builder: (context, value, child) { - return Scaffold( - body: appState.questionsList.isEmpty - ? Row( - children: [ - Container( - color: themeApp.colorScheme.primaryContainer, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context - .read() - .clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )), - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column( - children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), - Text('Exemples de questions...', style: titleText2), - const SizedBox(height: 5), - Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), - ], - ), - ), - ), - ], - ) - : - - // Non-empty list case - Row( - children: [ - Container( - color: themeApp.colorScheme.primaryContainer, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context - .read() - .clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )), - Expanded( - child: Container( - color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding( - padding: const EdgeInsets.all(20), - child: textField), - ]), - ), - ), - ], - ), - ); - }); - } -} - -class MyTextField extends StatefulWidget { - const MyTextField({Key? key}) : super(key: key); - - @override - State createState() => _MyTextFieldState(); -} - -class _MyTextFieldState extends State { - final _textController = TextEditingController(); - String userQuestion = ''; - - @override - Widget build(BuildContext context) { - return TextField( - autocorrect: true, - controller: _textController, - style: normalText, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'Posez votre question ici...', - labelStyle: normalText, - suffixIcon: IconButton( - color: themeApp.colorScheme.onPrimary, - onPressed: () { - setState(() { - userQuestion = _textController.text; - context.read().addQuestion(userQuestion); - _textController.clear(); - }); - }, - icon: const Icon(Icons.send))), - ); - } -} - -class QuestionGrid extends StatefulWidget { - QuestionGrid({Key? key}) : super(key: key); - - @override - State createState() => _QuestionGridState(); -} - -class _QuestionGridState extends State { - final List exemples = [ - "Qu'est-ce que le Covid-19 ?", - "Quels sont les symptômes du Covid-19 ?", - "Comment se transmet le Covid-19 ?", - "Quels sont les risques de contamination ?", - "Quel est la taille moyenne d'une famille ?", - "Quel est l'âge moyen d'un crabe ?", - "Quel est le nombre de personnes qui ont été contaminées ?", - "Pourquoi 42 ?", - "Quand est-ce que le monde va mourir ?", - ]; - - @override - Widget build(BuildContext context) { - List questions = []; - - // Generate a random array of questions - String userQuestion = ""; - for (int i = 1; i <= 4; i++) { - questions.add( - Container( - color: Colors.grey[300], - child: TextButton( - onPressed: () { - setState(() { - userQuestion = exemples[Random().nextInt(exemples.length)]; - context.read().addQuestion(userQuestion); - }); - }, - child: Text( - exemples[Random().nextInt(exemples.length)], - textAlign: TextAlign.center, - ), - )), - ); - } - - // Display - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Padding( - padding: const EdgeInsets.only(right: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.help_center_outlined, - color: themeApp.colorScheme.onPrimary, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(left: 12, bottom: 10, top: 10), - child: Center( - child: Icon( - Icons.question_answer, - color: themeApp.colorScheme.onPrimary, - ), - ), - ), - ], - ), - Expanded( - child: GridView.count( - padding: const EdgeInsets.only(right: 20, left: 20), - primary: false, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - crossAxisCount: 2, - children: questions, - ), - ), - ], - ); - } -} - -class History extends StatefulWidget { - const History({Key? key}) : super(key: key); - - @override - State createState() => _HistoryState(); -} - -class _HistoryState extends State { - final _key = GlobalKey(); - final DatabaseService _databaseService = DatabaseService(); - - @override - Widget build(BuildContext context) { - final appState = context.watch(); - appState.historyKey = _key; - - return StreamBuilder>>( - stream: _databaseService.getData(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError) { - return const Center(child: Text('An error has occurred')); - } - - final reponseList = snapshot.data ?? []; - final LocalResponseList localResponseList = LocalResponseList(); - localResponseList.addResponseListFromMap(reponseList); - - return AnimatedList( - key: _key, - reverse: true, - initialItemCount: appState.questionsList.length, - itemBuilder: (context, index, animation) { - final question = appState.questionsList[index]; - return SizeTransition( - sizeFactor: animation, - child: Center( - child: Column( - children: [ - const SizedBox(height: 10), - Container( - // * QUESTION * // - color: themeApp.colorScheme.primaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - question, - style: titleText2, - textAlign: TextAlign.center, - )), - const SizedBox(height: 10), - Container( - // * RESPONSES * // - color: themeApp.colorScheme.secondaryContainer, - padding: const EdgeInsets.all(10), - child: Text( - localResponseList.getRandomResponseWithWeights().text, - style: titleText2, - textAlign: TextAlign.center, - ), - ), - ], - ), - ), - ); - }, - ); - }, - ); - } -} diff --git a/pubspec.lock b/pubspec.lock index 0c04c2b..249094a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,14 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - archive: - dependency: transitive - description: - name: archive - sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d - url: "https://pub.dev" - source: hosted - version: "3.3.6" _fe_analyzer_shared: dependency: transitive description: @@ -33,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.7.1" + archive: + dependency: transitive + description: + name: archive + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" + source: hosted + version: "3.3.6" args: dependency: transitive description: @@ -129,7 +129,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - dart_style: dependency: transitive description: @@ -252,15 +251,6 @@ packages: description: flutter source: sdk version: "0.0.0" - image: - dependency: transitive - description: - name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" - url: "https://pub.dev" - source: hosted - version: "3.3.0" - flutter_web_plugins: dependency: transitive description: flutter @@ -274,6 +264,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + image: + dependency: transitive + description: + name: image + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" + source: hosted + version: "3.3.0" js: dependency: transitive description: @@ -362,14 +360,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" - pointycastle: - dependency: transitive - description: - name: pointycastle - sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c - url: "https://pub.dev" - source: hosted - version: "3.7.2" plugin_platform_interface: dependency: transitive description: @@ -378,6 +368,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + url: "https://pub.dev" + source: hosted + version: "3.7.2" provider: dependency: "direct main" description: @@ -471,14 +469,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - xml: - dependency: transitive - description: - name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" - url: "https://pub.dev" - source: hosted - version: "6.2.2" watcher: dependency: transitive description: @@ -487,6 +477,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" yaml: dependency: transitive description: @@ -496,5 +494,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.19.0 <4.0.0" flutter: ">=2.0.0" From a49b5bb64f9913e9de0180bd2893e09167040326 Mon Sep 17 00:00:00 2001 From: IzaakAM Date: Mon, 27 Mar 2023 23:34:51 +0200 Subject: [PATCH 36/37] Front improve --- lib/main page/components/Drawer.dart | 6 +++--- lib/main page/components/History.dart | 4 ---- lib/main page/components/QuestionsGrid.dart | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart index b8b99f8..b441896 100644 --- a/lib/main page/components/Drawer.dart +++ b/lib/main page/components/Drawer.dart @@ -13,14 +13,14 @@ class MyDrawer extends StatelessWidget { context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Merci !'), - content: const Text('Merci pour votre contribution!'), + content: const Text('Merci pour votre contribution !'), + icon: const Icon(Icons.check_circle_outline_outlined), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: const Text('Fermer'), + child: Text('Fermer', style: normalText), ), ], ); diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index ddb84be..1ff5715 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import '../../main.dart'; import '../../constants.dart'; @@ -28,8 +26,6 @@ class _HistoryState extends State { Widget build(BuildContext context) { final appState = context.watch(); appState.historyKey = _key; - double largeurEcran = MediaQuery.of(context).size.width; - return ListView.builder( reverse: true, itemCount: appState.questionsList.length, diff --git a/lib/main page/components/QuestionsGrid.dart b/lib/main page/components/QuestionsGrid.dart index 17de206..398a106 100644 --- a/lib/main page/components/QuestionsGrid.dart +++ b/lib/main page/components/QuestionsGrid.dart @@ -36,7 +36,7 @@ class _QuestionGridState extends State { Container( decoration: BoxDecoration( color: themeApp.colorScheme.secondaryContainer.withOpacity(1), - borderRadius: BorderRadius.circular(15)), + borderRadius: BorderRadius.circular(10)), child: TextButton( onPressed: () { setState(() { From e397ea709506e5cbba49d587687baaeb8a382b58 Mon Sep 17 00:00:00 2001 From: Robin JOSEPH Date: Tue, 28 Mar 2023 01:39:38 +0200 Subject: [PATCH 37/37] All seems working for the first release, add the aibility to update thumb scores and contribute. --- lib/main page/components/Drawer.dart | 5 ++ lib/main page/components/History.dart | 41 +++++++---- ...QuestionsGrid.dart => questions_grid.dart} | 0 .../{TextField.dart => text_field.dart} | 25 +++++-- lib/main page/main_page.dart | 69 ++++--------------- lib/response.dart | 62 ++++++++--------- 6 files changed, 95 insertions(+), 107 deletions(-) rename lib/main page/components/{QuestionsGrid.dart => questions_grid.dart} (100%) rename lib/main page/components/{TextField.dart => text_field.dart} (71%) diff --git a/lib/main page/components/Drawer.dart b/lib/main page/components/Drawer.dart index 9ef42eb..ea2212d 100644 --- a/lib/main page/components/Drawer.dart +++ b/lib/main page/components/Drawer.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../constants.dart'; +import '../../database_service.dart'; +import '../../main.dart'; class MyDrawer extends StatelessWidget { const MyDrawer({Key? key}) : super(key: key); @@ -29,6 +31,7 @@ class MyDrawer extends StatelessWidget { //! dialog window of contribution void _showContributionDialog(BuildContext context) { + final DatabaseService _databaseService = DatabaseService(); final TextEditingController _controller = TextEditingController(); showDialog( context: context, @@ -49,6 +52,8 @@ class MyDrawer extends StatelessWidget { ), TextButton( onPressed: () { + // Ajoute la nouvelle réponse à la base de données + _databaseService.addResponse(_controller.text, 0, 0); Navigator.of(context) .pop(); // Ferme la boîte de dialogue de contribution _showThankYouDialog( diff --git a/lib/main page/components/History.dart b/lib/main page/components/History.dart index 6ec533b..295a01c 100644 --- a/lib/main page/components/History.dart +++ b/lib/main page/components/History.dart @@ -1,8 +1,9 @@ +import 'dart:math'; + import '../../database_service.dart'; import '../../main.dart'; import '../../constants.dart'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -18,6 +19,7 @@ class History extends StatefulWidget { class _HistoryState extends State { final _key = GlobalKey(); final DatabaseService _databaseService = DatabaseService(); + List selectedResponses = []; @override Widget build(BuildContext context) { @@ -39,14 +41,19 @@ class _HistoryState extends State { final LocalResponseList localResponseList = LocalResponseList(); localResponseList.addResponseListFromMap(reponseList); - return ListView.builder( - reverse: true, - itemCount: appState.questionsList.length, - itemBuilder: (context, index) { - final question = appState.questionsList[index]; - return Center( - child: Column( - children: [ + return ListView.builder( + reverse: true, + itemCount: appState.questionsList.length, + itemBuilder: (context, index) { + final question = appState.questionsList[index]; + if (index >= selectedResponses.length) { + selectedResponses.insert( + 0, localResponseList.getRandomResponseWithWeights()); + print(selectedResponses); + } + final randomResponse = selectedResponses[index]; + return Center( + child: Column(children: [ // space between 2 [questions/responses] const SizedBox(height: 0), @@ -81,7 +88,7 @@ class _HistoryState extends State { SizedBox(height: 10), // * Response Text( - reponseList[Random().nextInt(reponseList.length)], + randomResponse.text, style: normalText, textAlign: TextAlign.center, ), @@ -91,7 +98,11 @@ class _HistoryState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - onPressed: () {}, + onPressed: () { + _databaseService.updateBlueThumb( + randomResponse.id, + randomResponse.blueThumb + 1); + }, icon: Icon( Icons.thumb_up, size: 18, @@ -99,7 +110,11 @@ class _HistoryState extends State { themeApp.colorScheme.onPrimaryContainer, )), IconButton( - onPressed: () {}, + onPressed: () { + _databaseService.updateRedThumb( + randomResponse.id, + randomResponse.redThumb + 1); + }, icon: Icon( Icons.thumb_down, size: 18, @@ -114,7 +129,7 @@ class _HistoryState extends State { ), ], ), - ); + ])); }, ); }, diff --git a/lib/main page/components/QuestionsGrid.dart b/lib/main page/components/questions_grid.dart similarity index 100% rename from lib/main page/components/QuestionsGrid.dart rename to lib/main page/components/questions_grid.dart diff --git a/lib/main page/components/TextField.dart b/lib/main page/components/text_field.dart similarity index 71% rename from lib/main page/components/TextField.dart rename to lib/main page/components/text_field.dart index c590ce1..a33953b 100644 --- a/lib/main page/components/TextField.dart +++ b/lib/main page/components/text_field.dart @@ -8,12 +8,21 @@ class MyTextField extends StatefulWidget { const MyTextField({Key? key}) : super(key: key); @override - State createState() => _MyTextFieldState(); + _MyTextFieldState createState() => _MyTextFieldState(); } class _MyTextFieldState extends State { - final _textController = TextEditingController(); - String userQuestion = ''; + TextEditingController _controller = TextEditingController(); + + void _sendMessage() { + String message = _controller.text.trim(); + if (message.isNotEmpty) { + print('Message envoyé: $message'); + var appState = context.read(); + appState.addQuestion(message); + _controller.clear(); + } + } @override Widget build(BuildContext context) { @@ -51,7 +60,15 @@ class _MyTextFieldState extends State { onSubmitted: (value) { _sendMessage(); }, - icon: const Icon(Icons.send))), + ), + ), + IconButton( + onPressed: _sendMessage, + icon: Icon(Icons.send, + color: themeApp.colorScheme.onPrimaryContainer), + ), + ], + ), ); } } diff --git a/lib/main page/main_page.dart b/lib/main page/main_page.dart index 4603e47..fbbd758 100644 --- a/lib/main page/main_page.dart +++ b/lib/main page/main_page.dart @@ -4,9 +4,10 @@ import 'package:provider/provider.dart'; import '../constants.dart'; import '../main.dart'; -import 'components/History.dart'; -import 'components/QuestionsGrid.dart'; -import 'components/TextField.dart'; +import 'components/history.dart'; +import 'components/questions_grid.dart'; +import 'components/drawer.dart'; +import 'components/text_field.dart'; class MainPage extends StatefulWidget { const MainPage({Key? key}) : super(key: key); @@ -17,17 +18,15 @@ class MainPage extends StatefulWidget { class _MainPageState extends State { // store the user's question - String userQuestion = ''; + String userPost = ''; @override Widget build(BuildContext context) { - // * Variables : var appState = context.watch(); // état de l'application MyDrawer myDrawer = const MyDrawer(); // le drawer MyTextField textField = const MyTextField(); - // Empty list case return Consumer(builder: (context, value, child) { return Scaffold( appBar: AppBar( @@ -53,62 +52,22 @@ class _MainPageState extends State { color: themeApp.colorScheme.background, child: Column( children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 5), + const SizedBox(height: 25), Text('Exemples de questions...', style: titleText2), const SizedBox(height: 5), Expanded(child: QuestionGrid()), - Padding( - padding: const EdgeInsets.only( - bottom: 25, top: 25, right: 15, left: 15), - child: textField), ], ), - ), - ), - ], - ) - : - - // Non-empty list case - Row( - children: [ - Container( - color: themeApp.colorScheme.primaryContainer, - width: 75, - child: Column( - children: [ - const Padding(padding: EdgeInsets.only(top: 75)), - IconButton( - iconSize: 35, - color: themeApp.colorScheme.onPrimaryContainer, - onPressed: () { - setState(() { - context - .read() - .clearQuestionList(); - }); - }, - icon: const Icon(Icons.refresh)) - ], - )), - Expanded( - child: Container( + ) + : Container( + // ! si la liste de questions n'est pas vide color: themeApp.colorScheme.background, - child: Column(children: [ - const SizedBox(height: 75), - Text('Chat ChuisPT', style: titleText), - const SizedBox(height: 25), - const Flexible(child: History()), - Padding( - padding: const EdgeInsets.all(20), - child: textField), - ]), + child: const Expanded(child: History()), ), - ), - ], - ), + ), + textField + ], + ), ); }); } diff --git a/lib/response.dart b/lib/response.dart index ca8b353..c893377 100644 --- a/lib/response.dart +++ b/lib/response.dart @@ -10,14 +10,13 @@ class LocalResponse { int usage = 0; double repetitionScore = 1.0; double globalScore = 1.0; + int lastUsedTimestamp = 0; LocalResponse( {required this.id, required this.text, required this.blueThumb, - required this.redThumb}) { - updateGlobalScore(); - } + required this.redThumb}); @override String toString() { @@ -45,33 +44,11 @@ class LocalResponse { void increaseUsage() { usage++; } - - void addUsage(int elementNumber) { - usage++; - repetitionScore /= elementNumber; - updateGlobalScore(); - } - - void addNonUsage(int elementNumber) { - repetitionScore *= (1 + 1 / elementNumber); - updateGlobalScore(); - } - - void updateGlobalScore() { - if (redThumb == 0) { - globalScore = blueThumb * repetitionScore; - } else if (blueThumb == 0) { - globalScore = 1 / redThumb * repetitionScore; - } else if (blueThumb == redThumb) { - globalScore = 1 * repetitionScore; - } else { - globalScore = blueThumb / redThumb * repetitionScore; - } - } } class LocalResponseList { static List listResponse = []; + static List recentlyUsedResponses = []; LocalResponseList(); @@ -107,19 +84,34 @@ class LocalResponseList { throw Exception("La liste est vide, aucune réponse à renvoyer."); } - double totalWeight = - listResponse.fold(0, (sum, item) => sum + item.globalScore); + const int maxRecentlyUsedResponses = + 3; // Nombre maximum de réponses à mémoriser + + List availableResponses = List.from(listResponse); + availableResponses + .removeWhere((response) => recentlyUsedResponses.contains(response)); + + if (availableResponses.isEmpty) { + recentlyUsedResponses.clear(); + availableResponses = List.from(listResponse); + } + + double totalWeight = availableResponses.fold(0, (sum, item) { + double itemWeight = (item.blueThumb + 1) / (item.redThumb + 1); + return sum + itemWeight; + }); + double randomWeight = Random().nextDouble() * totalWeight; double cumulativeWeight = 0; - for (var response in listResponse) { - cumulativeWeight += response.globalScore; + for (var response in availableResponses) { + double itemWeight = (response.blueThumb + 1) / (response.redThumb + 1); + cumulativeWeight += itemWeight; + if (randomWeight <= cumulativeWeight) { - response.addUsage(listResponse.length); - for (var otherResponse in listResponse) { - if (otherResponse != response) { - otherResponse.addNonUsage(listResponse.length); - } + recentlyUsedResponses.add(response); + if (recentlyUsedResponses.length > maxRecentlyUsedResponses) { + recentlyUsedResponses.removeAt(0); } if (kDebugMode) { print(response);