From f69c8ad4fde9ce7d94eaf926b5b875d241c037c4 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Wed, 10 Jan 2024 23:18:46 -0500 Subject: [PATCH 01/11] fix: running on android device and update dependencies --- firebase.json | 3 +- firestore.rules | 2 +- lib/main.dart | 7 +- lib/presentation/screens/home/home.dart | 2 +- pubspec.lock | 90 ++++++++++--------- pubspec.yaml | 12 +-- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 8 files changed, 64 insertions(+), 56 deletions(-) diff --git a/firebase.json b/firebase.json index 9df8d43..c2e17a3 100644 --- a/firebase.json +++ b/firebase.json @@ -1,7 +1,8 @@ { "emulators": { "firestore": { - "port": 8080 + "port": 8080, + "host": "0.0.0.0" }, "ui": { "enabled": true diff --git a/firestore.rules b/firestore.rules index 71bd24b..a3eeb31 100644 --- a/firestore.rules +++ b/firestore.rules @@ -13,7 +13,7 @@ service cloud.firestore { // all client requests to your Firestore database will be denied until you Update // your rules match /{document=**} { - allow read, write: if request.time < timestamp.date(2023, 7, 26); + allow read, write: if request.time < timestamp.date(2024, 7, 26); } } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index d4540ff..dc98d53 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,12 +16,7 @@ void main() async { if (kDebugMode) { try { - FirebaseFirestore.instance.useFirestoreEmulator( - defaultTargetPlatform == TargetPlatform.android - ? "10.0.2.2" - : "localhost", - 8080, - ); + FirebaseFirestore.instance.useFirestoreEmulator('0.0.0.0', 8080); } catch (e) { // ignore: avoid_print print(e); diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index c47bce4..8b080f4 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -76,7 +76,7 @@ class _MyHomePageState extends State { 'https://i1.wp.com/www.suitcasescholar.com/wp-content/uploads/2012/08/DSC_2583.jpg', ), ); - setState(() {}); + setState(() {/*The List has changed*/}); }, child: const Icon(Icons.add), ), diff --git a/pubspec.lock b/pubspec.lock index 5865055..f15bae1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: a742f71d7f3484253a623b30e19256aa4668ecbb3de6ad1beb0bcf8d4777ecd8 + sha256: f5628cd9c92ed11083f425fd1f8f1bc60ecdda458c81d73b143aeda036c35fe7 url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.16" async: dependency: transitive description: @@ -45,34 +45,34 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: "988351d4fcc58c47578d95d014018888b2ce7a228f84ce322fea4a127707a0d4" + sha256: "8bfbb5a2edbc6052452326d60de0113fea2bcbf081d34a3f8e45c8b38307b31c" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.14.0" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: b6652ce95507e604f00cb0c9c9be2363d21746e82667f2f3d61edf2d33cad3bf + sha256: "73ff438fe46028f0e19f55da18b6ddc6906ab750562cd7d9ffab77ff8c0c4307" url: "https://pub.dev" source: hosted - version: "5.15.1" + version: "6.1.0" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "22d02595eb7a304c0f1b4a717e78cc054522e8f237eb7b1122886f93130f3f7a" + sha256: "232e45e95970d3a6baab8f50f9c3a6e2838d145d9d91ec9a7392837c44296397" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "3.9.0" collection: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" crypto: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" fake_async: dependency: transitive description: @@ -117,26 +117,26 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: a4a99204da264a0aa9d54a332ea0315ce7b0768075139c77abefe98093dd98be + sha256: "96607c0e829a581c2a483c658f04e8b159964c3bae2730f73297070bc85d40bb" url: "https://pub.dev" source: hosted - version: "2.14.0" + version: "2.24.2" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "0fd5c4b228de29b55fac38aed0d9e42514b3d3bd47675de52bf7f8fccaf922fa" + sha256: d585bdf3c656c3f7821ba1bd44da5f13365d22fcecaf5eb75c4295246aaa83c0 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.10.0" flutter: dependency: "direct main" description: flutter @@ -146,10 +146,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" flutter_localizations: dependency: "direct main" description: flutter @@ -169,10 +169,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: db5efba8106bd784a92c96cfd81716f4e06baab54e37de858488e9a00a764cad + sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.1.0" http: dependency: transitive description: @@ -193,10 +193,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" js: dependency: transitive description: @@ -209,34 +209,34 @@ packages: dependency: transitive description: name: lints - sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "3.0.0" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -326,26 +326,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -366,10 +366,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" typed_data: dependency: transitive description: @@ -386,6 +386,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" win32: dependency: transitive description: @@ -403,5 +411,5 @@ packages: source: hosted version: "1.0.0" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.3 <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5c6a284..df7725a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,23 +4,23 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: ^3.0.0 + sdk: ^3.2.3 dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter - intl: any - cupertino_icons: ^1.0.5 - google_fonts: ^5.0.0 - cloud_firestore: ^4.8.1 + intl: ^0.18.1 + cupertino_icons: ^1.0.6 + google_fonts: ^6.1.0 + cloud_firestore: ^4.14.0 firebase_core: ^2.14.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^3.0.1 flutter: uses-material-design: true diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1a82e7d..eeeeb11 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,12 @@ #include "generated_plugin_registrant.h" +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + CloudFirestorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index fa8a39b..448a2c3 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + cloud_firestore firebase_core ) From b12caeed67aa42a5b4d10c67762e83e6dbadc3db Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Thu, 11 Jan 2024 07:26:48 -0500 Subject: [PATCH 02/11] chore: wip, add modal sheet layout and image picker --- .../components/src/add_post_modal_sheet.dart | 132 ++++++++++++++++++ lib/presentation/screens/home/home.dart | 26 ++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 122 +++++++++++++++- pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 7 files changed, 277 insertions(+), 10 deletions(-) create mode 100644 lib/presentation/components/src/add_post_modal_sheet.dart diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart new file mode 100644 index 0000000..0d597a3 --- /dev/null +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -0,0 +1,132 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; + +class AddPostModalPage extends StatelessWidget { + AddPostModalPage({super.key}); + + final TextEditingController textEditName = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + const Align(alignment: Alignment.centerRight, child: CloseButton()), + const ImagePickerView(), + TextField( + decoration: const InputDecoration( + label: Text('title'), + ), + controller: textEditName, + ), + const SizedBox(height: 16,), + Row( + children: [ + OutlinedButton(onPressed: () { + + //Firestore + }, child: const Text('Upload Picture')), + //loading + ], + ) + ], + ), + ); + } +} + +class ImagePickerView extends StatefulWidget { + const ImagePickerView({super.key}); + + @override + State createState() => _ImagePickerViewState(); +} + +class _ImagePickerViewState extends State { + final double? iconSize = 96; + final ImagePicker picker = ImagePicker(); + XFile? image; + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: 4 / 3, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Colors.grey, + style: BorderStyle.solid, + ), + ), + child: image == null + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: () async { + image = await picker.pickImage( + source: ImageSource.gallery); + }, + icon: Icon( + Icons.photo, + size: iconSize, + ), + ), + const Text('Select Picture') + ], + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Text('OR')), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: () async { + image = await picker.pickImage( + source: ImageSource.camera); + setState(() {}); + }, + icon: Icon( + Icons.photo_camera, + size: iconSize, + ), + ), + const Text('Take Picture') + ], + ), + ], + ) + : ClipRRect( + borderRadius: BorderRadius.circular(16.0), + child: Stack( + fit: StackFit.expand, + children: [ + Image.file( + File(image!.path), + fit: BoxFit.cover, + ), + Align( + alignment: Alignment.bottomRight, + child: IconButton( + onPressed: () { + image = null; + setState(() {}); + }, + icon: const Icon(Icons.delete), + )) + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index 8b080f4..5748f31 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -5,6 +5,7 @@ import 'package:instreal/features/posts/posts_firestore.dart'; import 'package:instreal/features/posts/posts_repository.dart'; import 'package:instreal/l10n/index.dart'; import 'package:instreal/presentation/components/index.dart'; +import 'package:instreal/presentation/components/src/add_post_modal_sheet.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -67,16 +68,23 @@ class _MyHomePageState extends State { ), floatingActionButton: FloatingActionButton( onPressed: () async { - await postingsRepo.add( - const Post( - id: "", - title: 'title', - author: 'author', - imageUrl: - 'https://i1.wp.com/www.suitcasescholar.com/wp-content/uploads/2012/08/DSC_2583.jpg', - ), + // returns a Post with the URL + showModalBottomSheet( + context: context, + builder: (context) => AddPostModalPage(), + isScrollControlled: true, + useSafeArea: true, ); - setState(() {/*The List has changed*/}); + // await postingsRepo.add( + // const Post( + // id: "", + // title: 'title', + // author: 'author', + // imageUrl: + // 'https://i1.wp.com/www.suitcasescholar.com/wp-content/uploads/2012/08/DSC_2583.jpg', + // ), + // ); + // setState(() {/*The List has changed*/}); }, child: const Icon(Icons.add), ), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6d06bd4..1a1e39a 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,11 +6,13 @@ import FlutterMacOS import Foundation import cloud_firestore +import file_selector_macos import firebase_core import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index f15bae1..89f4d91 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" crypto: dependency: transitive description: @@ -113,6 +121,38 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + url: "https://pub.dev" + source: hosted + version: "0.9.3+3" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" firebase_core: dependency: "direct main" description: @@ -155,6 +195,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + url: "https://pub.dev" + source: hosted + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter @@ -189,6 +237,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" + url: "https://pub.dev" + source: hosted + version: "1.0.7" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" + url: "https://pub.dev" + source: hosted + version: "0.8.9+3" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: eac0a62104fa12feed213596df0321f57ce5a572562f72a68c4ff81e9e4caacf + url: "https://pub.dev" + source: hosted + version: "0.8.9" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e" + url: "https://pub.dev" + source: hosted + version: "2.9.2" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" intl: dependency: "direct main" description: @@ -237,6 +349,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" path: dependency: transitive description: @@ -412,4 +532,4 @@ packages: version: "1.0.0" sdks: dart: ">=3.2.3 <4.0.0" - flutter: ">=3.3.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index df7725a..8aa6e95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: google_fonts: ^6.1.0 cloud_firestore: ^4.14.0 firebase_core: ^2.14.0 + image_picker: ^1.0.7 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index eeeeb11..ffc1b38 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { CloudFirestorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 448a2c3..3cd4602 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST cloud_firestore + file_selector_windows firebase_core ) From 7a33d9fb4291f87ba47299d8d7208424788b9b21 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Thu, 11 Jan 2024 08:48:31 -0500 Subject: [PATCH 03/11] chore: wip, update add post prototype and small fix --- .../components/src/add_post_modal_sheet.dart | 27 ++++++++++++++----- lib/presentation/screens/home/home.dart | 19 +++++-------- linux/flutter/generated_plugin_registrant.cc | 4 +++ linux/flutter/generated_plugins.cmake | 1 + 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index 0d597a3..7d369e4 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:instreal/features/posts/post_entity.dart'; class AddPostModalPage extends StatelessWidget { AddPostModalPage({super.key}); @@ -22,14 +23,25 @@ class AddPostModalPage extends StatelessWidget { ), controller: textEditName, ), - const SizedBox(height: 16,), + const SizedBox(height: 16), Row( children: [ - OutlinedButton(onPressed: () { + OutlinedButton( + onPressed: () { + //TODO add Firestore cloud storage - //Firestore - }, child: const Text('Upload Picture')), - //loading + Navigator.pop( + context, + Post( + id: '', + title: textEditName.text, + author: 'author',// get name from firebase auth V3 + imageUrl: '',// get url from firestore + ), + ); + }, + child: const Text('Upload Picture')), + //add loading indicator when uploading ], ) ], @@ -73,6 +85,7 @@ class _ImagePickerViewState extends State { onPressed: () async { image = await picker.pickImage( source: ImageSource.gallery); + setState(() {/*Update Image*/}); }, icon: Icon( Icons.photo, @@ -93,7 +106,7 @@ class _ImagePickerViewState extends State { onPressed: () async { image = await picker.pickImage( source: ImageSource.camera); - setState(() {}); + setState(() {/*Update Image*/}); }, icon: Icon( Icons.photo_camera, @@ -119,7 +132,7 @@ class _ImagePickerViewState extends State { child: IconButton( onPressed: () { image = null; - setState(() {}); + setState(() {/*Remove Image*/}); }, icon: const Icon(Icons.delete), )) diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index 5748f31..32e3491 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -68,23 +68,18 @@ class _MyHomePageState extends State { ), floatingActionButton: FloatingActionButton( onPressed: () async { - // returns a Post with the URL - showModalBottomSheet( + // returns a Post + final post = await showModalBottomSheet( context: context, builder: (context) => AddPostModalPage(), isScrollControlled: true, useSafeArea: true, ); - // await postingsRepo.add( - // const Post( - // id: "", - // title: 'title', - // author: 'author', - // imageUrl: - // 'https://i1.wp.com/www.suitcasescholar.com/wp-content/uploads/2012/08/DSC_2583.jpg', - // ), - // ); - // setState(() {/*The List has changed*/}); + + if (post != null) { + await postingsRepo.add(post); + setState(() {/*The List has changed*/}); + } }, child: const Icon(Icons.add), ), diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..64a0ece 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..2db3c22 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From ded4d0fcefbd0b41fe442a2042b1e66b1adccd3c Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Fri, 12 Jan 2024 05:09:10 -0500 Subject: [PATCH 04/11] chore: setup firebase storage emulator --- firebase.json | 9 ++++++- lib/main.dart | 5 +++- .../components/src/add_post_modal_sheet.dart | 2 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ pubspec.lock | 24 +++++++++++++++++++ pubspec.yaml | 1 + storage.rules | 12 ++++++++++ .../flutter/generated_plugin_registrant.cc | 3 +++ windows/flutter/generated_plugins.cmake | 1 + 9 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 storage.rules diff --git a/firebase.json b/firebase.json index c2e17a3..c222b16 100644 --- a/firebase.json +++ b/firebase.json @@ -7,10 +7,17 @@ "ui": { "enabled": true }, - "singleProjectMode": true + "singleProjectMode": true, + "storage": { + "port": 9199, + "host": "0.0.0.0" + } }, "firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" + }, + "storage": { + "rules": "storage.rules" } } diff --git a/lib/main.dart b/lib/main.dart index dc98d53..d7af138 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -16,7 +17,9 @@ void main() async { if (kDebugMode) { try { - FirebaseFirestore.instance.useFirestoreEmulator('0.0.0.0', 8080); + const host = '0.0.0.0'; + FirebaseStorage.instance.useStorageEmulator(host, 9199); + FirebaseFirestore.instance.useFirestoreEmulator(host, 8080); } catch (e) { // ignore: avoid_print print(e); diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index 7d369e4..fb3fbaf 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -16,7 +16,7 @@ class AddPostModalPage extends StatelessWidget { child: Column( children: [ const Align(alignment: Alignment.centerRight, child: CloseButton()), - const ImagePickerView(), + const ImagePickerView(), // add condition here instead of inside TextField( decoration: const InputDecoration( label: Text('title'), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 1a1e39a..e566b43 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,11 +8,13 @@ import Foundation import cloud_firestore import file_selector_macos import firebase_core +import firebase_storage import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 89f4d91..b20bcd1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -177,6 +177,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: "75e6cb6bed65138b5bbd86bfd7cf9bc9a175fb0c31aacc400e9203df117ffbe6" + url: "https://pub.dev" + source: hosted + version: "11.6.0" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "545a3a8edf337850403bb0fa03c8074a53deb87c0107d19755c77a82ce07919e" + url: "https://pub.dev" + source: hosted + version: "5.1.3" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: ee6870ff79aa304b8996ba18a4aefe1e8b3fc31fd385eab6574180267aa8d393 + url: "https://pub.dev" + source: hosted + version: "3.6.17" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 8aa6e95..9fa4b23 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: cloud_firestore: ^4.14.0 firebase_core: ^2.14.0 image_picker: ^1.0.7 + firebase_storage: ^11.6.0 dev_dependencies: flutter_test: diff --git a/storage.rules b/storage.rules new file mode 100644 index 0000000..8553198 --- /dev/null +++ b/storage.rules @@ -0,0 +1,12 @@ +rules_version = '2'; + +// Craft rules based on data in your Firestore database +// allow write: if firestore.get( +// /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin; +service firebase.storage { + match /b/{bucket}/o { + match /{allPaths=**} { + allow read, write: if false; + } + } +} \ No newline at end of file diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index ffc1b38..ca05a30 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { CloudFirestorePluginCApiRegisterWithRegistrar( @@ -17,4 +18,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FileSelectorWindows")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + FirebaseStoragePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseStoragePluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 3cd4602..ec13ce6 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST cloud_firestore file_selector_windows firebase_core + firebase_storage ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 1dba834562cc8072f0652ca65552015e2c0df78c Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Fri, 12 Jan 2024 18:57:37 -0500 Subject: [PATCH 05/11] chore: add firebase storage and prototype implementation in modal layout --- .../components/src/add_post_modal_sheet.dart | 223 +++++++++++------- lib/presentation/screens/home/home.dart | 6 +- storage.rules | 2 +- 3 files changed, 140 insertions(+), 91 deletions(-) diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index fb3fbaf..d814829 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -1,12 +1,21 @@ import 'dart:io'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:instreal/features/posts/post_entity.dart'; -class AddPostModalPage extends StatelessWidget { - AddPostModalPage({super.key}); +class AddPostModalPage extends StatefulWidget { + final Reference storageRef; + const AddPostModalPage({required this.storageRef, super.key}); + @override + State createState() => _AddPostModalPageState(); +} + +class _AddPostModalPageState extends State { + XFile? image; + final ImagePicker picker = ImagePicker(); final TextEditingController textEditName = TextEditingController(); @override @@ -16,7 +25,25 @@ class AddPostModalPage extends StatelessWidget { child: Column( children: [ const Align(alignment: Alignment.centerRight, child: CloseButton()), - const ImagePickerView(), // add condition here instead of inside + if (image != null) + ImageView( + imagePath: image!.path, + onPressedRemoveImage: () { + image = null; + setState(() {/*Remove Image*/}); + }, + ) + else + ImagePickerView( + onPressedCamera: () async { + image = await picker.pickImage(source: ImageSource.camera); + setState(() {/*Update Image*/}); + }, + onPressedGallery: () async { + image = await picker.pickImage(source: ImageSource.gallery); + setState(() {/*Update Image*/}); + }, + ), TextField( decoration: const InputDecoration( label: Text('title'), @@ -27,18 +54,31 @@ class AddPostModalPage extends StatelessWidget { Row( children: [ OutlinedButton( - onPressed: () { - //TODO add Firestore cloud storage + onPressed: () async { + final storage = + widget.storageRef.child('images/instreal.jpg'); + + late String fileUrl; + //TODO may need a proper implementation + + try { + await storage.putFile(File(image!.path)); + fileUrl = await storage.getDownloadURL(); + } on FirebaseException catch (e) { + debugPrint(e.message); + } - Navigator.pop( - context, - Post( - id: '', - title: textEditName.text, - author: 'author',// get name from firebase auth V3 - imageUrl: '',// get url from firestore - ), - ); + if (context.mounted) { + Navigator.pop( + context, + Post( + id: '', + title: textEditName.text, + author: 'author', // get name from firebase auth V3 + imageUrl: fileUrl, // get url from firestore + ), + ); + } }, child: const Text('Upload Picture')), //add loading indicator when uploading @@ -50,95 +90,100 @@ class AddPostModalPage extends StatelessWidget { } } -class ImagePickerView extends StatefulWidget { - const ImagePickerView({super.key}); +class ImagePickerView extends StatelessWidget { + final double? iconSize = 96; + final VoidCallback onPressedCamera; + final VoidCallback onPressedGallery; - @override - State createState() => _ImagePickerViewState(); -} + const ImagePickerView({ + required this.onPressedCamera, + required this.onPressedGallery, + super.key, + }); -class _ImagePickerViewState extends State { - final double? iconSize = 96; - final ImagePicker picker = ImagePicker(); - XFile? image; @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 4 / 3, child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.0), - border: Border.all( - color: Colors.grey, - style: BorderStyle.solid, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Colors.grey, + style: BorderStyle.solid, + ), ), - ), - child: image == null - ? Row( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton.outlined( - padding: const EdgeInsets.all(16), - onPressed: () async { - image = await picker.pickImage( - source: ImageSource.gallery); - setState(() {/*Update Image*/}); - }, - icon: Icon( - Icons.photo, - size: iconSize, - ), - ), - const Text('Select Picture') - ], - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Text('OR')), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton.outlined( - padding: const EdgeInsets.all(16), - onPressed: () async { - image = await picker.pickImage( - source: ImageSource.camera); - setState(() {/*Update Image*/}); - }, - icon: Icon( - Icons.photo_camera, - size: iconSize, - ), - ), - const Text('Take Picture') - ], + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: onPressedGallery, + icon: Icon( + Icons.photo, + size: iconSize, + ), ), + const Text('Select Picture') ], - ) - : ClipRRect( - borderRadius: BorderRadius.circular(16.0), - child: Stack( - fit: StackFit.expand, - children: [ - Image.file( - File(image!.path), - fit: BoxFit.cover, + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Text('OR')), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: onPressedCamera, + icon: Icon( + Icons.photo_camera, + size: iconSize, ), - Align( - alignment: Alignment.bottomRight, - child: IconButton( - onPressed: () { - image = null; - setState(() {/*Remove Image*/}); - }, - icon: const Icon(Icons.delete), - )) - ], - ), + ), + const Text('Take Picture') + ], + ), + ], + )), + ); + } +} + +class ImageView extends StatelessWidget { + final VoidCallback onPressedRemoveImage; + final String imagePath; + const ImageView({ + required this.imagePath, + required this.onPressedRemoveImage, + super.key, + }); + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: 4 / 3, + child: ClipRRect( + borderRadius: BorderRadius.circular(16.0), + child: Stack( + fit: StackFit.expand, + children: [ + Image.file( + File(imagePath), + fit: BoxFit.cover, + ), + Align( + alignment: Alignment.bottomRight, + child: IconButton( + onPressed: onPressedRemoveImage, + icon: const Icon(Icons.delete), ), + ) + ], + ), ), ); } diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index 32e3491..89e2778 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -1,3 +1,4 @@ +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:instreal/features/posts/post_entity.dart'; @@ -16,6 +17,9 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final postingsRepo = PostsRepositoryImpl(firestore: PostFirestoreImpl()); + // need to define an Impl like a repository + final storageRef = FirebaseStorage.instance.ref(); + @override Widget build(BuildContext context) { @@ -71,7 +75,7 @@ class _MyHomePageState extends State { // returns a Post final post = await showModalBottomSheet( context: context, - builder: (context) => AddPostModalPage(), + builder: (context) => AddPostModalPage(storageRef: storageRef), isScrollControlled: true, useSafeArea: true, ); diff --git a/storage.rules b/storage.rules index 8553198..cdbd6b2 100644 --- a/storage.rules +++ b/storage.rules @@ -6,7 +6,7 @@ rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { - allow read, write: if false; + allow read, write: if true; // TODO needs proper rule } } } \ No newline at end of file From dad115c46705ede104d159808ae27a9a5c0405c4 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Sat, 13 Jan 2024 00:03:39 -0500 Subject: [PATCH 06/11] fix: add SafeArea under Scaffold for device ui --- .../components/src/add_post_modal_sheet.dart | 2 +- lib/presentation/screens/home/home.dart | 82 ++++++++++--------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index d814829..bcc4fc9 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -75,7 +75,7 @@ class _AddPostModalPageState extends State { id: '', title: textEditName.text, author: 'author', // get name from firebase auth V3 - imageUrl: fileUrl, // get url from firestore + imageUrl: fileUrl, ), ); } diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index 89e2778..8a4286b 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -24,50 +24,52 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: NestedScrollView( - headerSliverBuilder: (context, innerBoxIsScrolled) => [ - SliverAppBar( - expandedHeight: 200, - collapsedHeight: 200, - flexibleSpace: FlexibleSpaceBar( - background: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 32.0), - Text( - InstrealLocalizations.of(context)!.whatsUpIn, - style: Theme.of(context).textTheme.displayLarge, - ), - Text( - InstrealLocalizations.of(context)!.montreal, - style: Theme.of(context).textTheme.displayLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - fontFamily: GoogleFonts.caveat().fontFamily, - ), - ), - ], + body: SafeArea( + child: NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) => [ + SliverAppBar( + expandedHeight: 200, + collapsedHeight: 200, + flexibleSpace: FlexibleSpaceBar( + background: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 32.0), + Text( + InstrealLocalizations.of(context)!.whatsUpIn, + style: Theme.of(context).textTheme.displayLarge, + ), + Text( + InstrealLocalizations.of(context)!.montreal, + style: Theme.of(context).textTheme.displayLarge!.copyWith( + color: Theme.of(context).colorScheme.primary, + fontFamily: GoogleFonts.caveat().fontFamily, + ), + ), + ], + ), ), ), + floating: false, + pinned: false, ), - floating: false, - pinned: false, + ], + body: FutureBuilder>( + future: postingsRepo.posts, + builder: (context, snapshot) => switch (snapshot) { + AsyncSnapshot(connectionState: ConnectionState.done, :final data) => + ListView.builder( + itemBuilder: (context, index) => PostCard(posting: data[index]), + itemCount: data!.length, + padding: const EdgeInsets.symmetric(horizontal: 16), + ), + _ => const Center( + child: CircularProgressIndicator(), + ), + }, ), - ], - body: FutureBuilder>( - future: postingsRepo.posts, - builder: (context, snapshot) => switch (snapshot) { - AsyncSnapshot(connectionState: ConnectionState.done, :final data) => - ListView.builder( - itemBuilder: (context, index) => PostCard(posting: data[index]), - itemCount: data!.length, - padding: const EdgeInsets.symmetric(horizontal: 16), - ), - _ => const Center( - child: CircularProgressIndicator(), - ), - }, ), ), floatingActionButton: FloatingActionButton( From eb60faefba5ae2186c8faae07695273479718a37 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Sat, 13 Jan 2024 19:08:33 -0500 Subject: [PATCH 07/11] docs: update README.md --- README.md | 61 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0a1ef42..42c5544 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,28 @@ The application requires the Firebase to run. The debug build connects to the lo firebase emulators:start ``` -**To disable the emulator in the dev environment, disable the line of codes in `lib/main.dart` or build in production mode** +### Connect to a Android Device + +1. Make sure firebase emulators is running. + +2. Connect your device via USB and enable developer mode. + +3. Head over to your Android SDK platform-tools folder `android_sdk/platform-tools/` + +4. Connect your Android device with Firebase Emulator Servers: + + Firestore + ```bash + adb reverse tcp:8080 tcp:8080 + ``` + Storage + ```bash + adb reverse tcp:9199 tcp:9199 + ``` +5. Run the app from your IDE. -```dart - if (kDebugMode) { - try { - FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080); - } catch (e) { - // ignore: avoid_print - print(e); - } - } -``` -### How to initialize Firebase +### How to initialize Firebase Production Reference: @@ -43,14 +51,39 @@ Reference: firebase login ``` -4. Install FlutterFire +4. Configure with your firebase project + ```bash + firebase init + ``` + +5. Install FlutterFire ```bash dart pub global activate flutterfire_cli ``` -5. Configure it using this command in the Project +6. Configure it using this command in the Project ```bash flutterfire configure ``` + +7. Disable the firebase emulator dev environment + +**disable the following lines of codes in `lib/main.dart` or build in production mode** +```dart + if (kDebugMode) { + try { + const host = '0.0.0.0'; + FirebaseStorage.instance.useStorageEmulator(host, 9199); + FirebaseFirestore.instance.useFirestoreEmulator(host, 8080); + } catch (e) { + // ignore: avoid_print + print(e); + } + } +``` + +## Support + +If you have trouble running this project. Go to our [discord group](https://discord.gg/NMc62wG7eU) and ask your questions under #stackoverflow channel. We'll be happy to lend you a hand. \ No newline at end of file From edb1d744e3a16da44f652f4299c9bb3676db9f21 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Sat, 13 Jan 2024 22:22:25 -0500 Subject: [PATCH 08/11] chore: add translation and update layout --- lib/l10n/intl_en.arb | 8 +- lib/l10n/intl_fr.arb | 8 +- .../components/src/add_post_modal_sheet.dart | 101 ++++++++++-------- 3 files changed, 70 insertions(+), 47 deletions(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1dc345b..da4d77f 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,5 +1,11 @@ { "@@locale": "en", "whatsUpIn": "What's up in", - "montreal": "Montréal" + "montreal": "Montreal", + "add":"Add", + "title":"title", + "upload":"Upload", + "selectPicture":"Select Picture", + "or":"OR", + "takePicture": "Take Picture" } diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index f15cabe..8b7fa23 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,5 +1,11 @@ { "@@locale": "fr", "whatsUpIn": "Quoi de neuf à", - "montreal": "Montréal" + "montreal": "Montréal", + "add": "Ajouter", + "title":"titre", + "upload":"Téléverser", + "selectPicture":"Choisir une Image", + "or":"OU", + "takePicture":"Prendre une Photo" } diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index bcc4fc9..c43d4a4 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:instreal/features/posts/post_entity.dart'; +import '../../../l10n/index.dart'; + class AddPostModalPage extends StatefulWidget { final Reference storageRef; const AddPostModalPage({required this.storageRef, super.key}); @@ -21,10 +23,18 @@ class _AddPostModalPageState extends State { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(16.0), child: Column( children: [ - const Align(alignment: Alignment.centerRight, child: CloseButton()), + Row( + children: [ + Text(InstrealLocalizations.of(context)!.add), + const Spacer(), + const Align( + alignment: Alignment.centerRight, child: CloseButton()), + ], + ), + const SizedBox(height: 8), if (image != null) ImageView( imagePath: image!.path, @@ -45,8 +55,8 @@ class _AddPostModalPageState extends State { }, ), TextField( - decoration: const InputDecoration( - label: Text('title'), + decoration: InputDecoration( + label: Text(InstrealLocalizations.of(context)!.title), ), controller: textEditName, ), @@ -80,7 +90,7 @@ class _AddPostModalPageState extends State { ); } }, - child: const Text('Upload Picture')), + child: Text(InstrealLocalizations.of(context)!.upload)), //add loading indicator when uploading ], ) @@ -106,49 +116,50 @@ class ImagePickerView extends StatelessWidget { return AspectRatio( aspectRatio: 4 / 3, child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.0), - border: Border.all( - color: Colors.grey, - style: BorderStyle.solid, - ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Colors.grey, + style: BorderStyle.solid, ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton.outlined( - padding: const EdgeInsets.all(16), - onPressed: onPressedGallery, - icon: Icon( - Icons.photo, - size: iconSize, - ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: onPressedGallery, + icon: Icon( + Icons.photo, + size: iconSize, ), - const Text('Select Picture') - ], - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Text('OR')), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton.outlined( - padding: const EdgeInsets.all(16), - onPressed: onPressedCamera, - icon: Icon( - Icons.photo_camera, - size: iconSize, - ), + ), + Text(InstrealLocalizations.of(context)!.selectPicture) + ], + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(InstrealLocalizations.of(context)!.or)), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton.outlined( + padding: const EdgeInsets.all(16), + onPressed: onPressedCamera, + icon: Icon( + Icons.photo_camera, + size: iconSize, ), - const Text('Take Picture') - ], - ), - ], - )), + ), + Text(InstrealLocalizations.of(context)!.takePicture) + ], + ), + ], + ), + ), ); } } From 358e22aaa838c20b085f8b3746ef26edca696532 Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Tue, 16 Jan 2024 17:21:06 -0500 Subject: [PATCH 09/11] fix: update host and remove auto mapping --- firebase.json | 6 ++---- lib/main.dart | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/firebase.json b/firebase.json index c222b16..c389860 100644 --- a/firebase.json +++ b/firebase.json @@ -1,16 +1,14 @@ { "emulators": { "firestore": { - "port": 8080, - "host": "0.0.0.0" + "port": 8080 }, "ui": { "enabled": true }, "singleProjectMode": true, "storage": { - "port": 9199, - "host": "0.0.0.0" + "port": 9199 } }, "firestore": { diff --git a/lib/main.dart b/lib/main.dart index d7af138..58dac6c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,9 +17,18 @@ void main() async { if (kDebugMode) { try { - const host = '0.0.0.0'; - FirebaseStorage.instance.useStorageEmulator(host, 9199); - FirebaseFirestore.instance.useFirestoreEmulator(host, 8080); + const host = 'localhost'; + const bool autoMapping = false; + FirebaseStorage.instance.useStorageEmulator( + host, + 9199, + automaticHostMapping: autoMapping, + ); + FirebaseFirestore.instance.useFirestoreEmulator( + host, + 8080, + automaticHostMapping: autoMapping, + ); } catch (e) { // ignore: avoid_print print(e); From 4e41554ad2c8b0c7fa48cecb0e9113cc35e98c5a Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Tue, 16 Jan 2024 17:48:48 -0500 Subject: [PATCH 10/11] docs: update README.md --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 42c5544..aa176b1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A new Flutter project. ## Getting Started -### How to start the Firebase emulator +### How to start the Firebase emulator environment The application requires the Firebase to run. The debug build connects to the local emulator by default while the production build connects to the production Firebase. @@ -36,7 +36,7 @@ The application requires the Firebase to run. The debug build connects to the lo 5. Run the app from your IDE. -### How to initialize Firebase Production +### How to initialize Firebase server environment Reference: @@ -72,11 +72,20 @@ Reference: **disable the following lines of codes in `lib/main.dart` or build in production mode** ```dart - if (kDebugMode) { +if (kDebugMode) { try { - const host = '0.0.0.0'; - FirebaseStorage.instance.useStorageEmulator(host, 9199); - FirebaseFirestore.instance.useFirestoreEmulator(host, 8080); + const host = 'localhost'; + const bool autoMapping = false; + FirebaseStorage.instance.useStorageEmulator( + host, + 9199, + automaticHostMapping: autoMapping, + ); + FirebaseFirestore.instance.useFirestoreEmulator( + host, + 8080, + automaticHostMapping: autoMapping, + ); } catch (e) { // ignore: avoid_print print(e); @@ -86,4 +95,4 @@ Reference: ## Support -If you have trouble running this project. Go to our [discord group](https://discord.gg/NMc62wG7eU) and ask your questions under #stackoverflow channel. We'll be happy to lend you a hand. \ No newline at end of file +If you have trouble running this project, open a [issue](https://github.com/fluttermtl/instreal/issues/new) and we'll be happy to lend you a hand. You could also ask questions to our discord group. \ No newline at end of file From 3824e3d311a846d6e7be866df4f55e665004e45f Mon Sep 17 00:00:00 2001 From: emmanuelCode Date: Tue, 16 Jan 2024 17:53:50 -0500 Subject: [PATCH 11/11] chore: change relative import to package import --- lib/presentation/components/src/add_post_modal_sheet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/components/src/add_post_modal_sheet.dart b/lib/presentation/components/src/add_post_modal_sheet.dart index c43d4a4..883216d 100644 --- a/lib/presentation/components/src/add_post_modal_sheet.dart +++ b/lib/presentation/components/src/add_post_modal_sheet.dart @@ -4,8 +4,8 @@ import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:instreal/features/posts/post_entity.dart'; +import 'package:instreal/l10n/index.dart'; -import '../../../l10n/index.dart'; class AddPostModalPage extends StatefulWidget { final Reference storageRef;