├── ios ├── Flutter │ ├── .last_build_id │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── GoogleService-Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── .gitignore └── Podfile ├── assets └── images │ ├── logo.png │ ├── beans.jpg │ ├── market.jpg │ ├── top_bg.png │ └── vegetables.png ├── lib ├── src │ ├── styles │ │ ├── buttons.dart │ │ ├── tabbar.dart │ │ ├── colors.dart │ │ ├── base.dart │ │ ├── textfields.dart │ │ └── text.dart │ ├── widgets │ │ ├── shopping_bag.dart │ │ ├── profile_customer.dart │ │ ├── orders.dart │ │ ├── navbar.dart │ │ ├── sliver_scaffold.dart │ │ ├── vendor_scaffold.dart │ │ ├── customer_scaffold.dart │ │ ├── profile.dart │ │ ├── social_button.dart │ │ ├── alerts.dart │ │ ├── list_tile.dart │ │ ├── card.dart │ │ ├── products_customer.dart │ │ ├── button.dart │ │ ├── textfield.dart │ │ ├── products.dart │ │ └── dropdown_button.dart │ ├── models │ │ ├── application_user.dart │ │ ├── vendor.dart │ │ ├── location.dart │ │ ├── market.dart │ │ └── product.dart │ ├── blocs │ │ ├── customer_bloc.dart │ │ ├── auth_bloc.dart │ │ ├── vendor_bloc.dart │ │ └── product_bloc.dart │ ├── services │ │ ├── firebase_storage_service.dart │ │ └── firestore_service.dart │ ├── screens │ │ ├── vendor.dart │ │ ├── customer.dart │ │ ├── landing.dart │ │ ├── signup.dart │ │ ├── login.dart │ │ ├── edit_vendor.dart │ │ └── edit_product.dart │ ├── app.dart │ └── routes.dart └── main.dart ├── android ├── gradle.properties ├── .gitignore ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── julow │ │ │ │ │ └── farmers_market │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── .metadata ├── .vscode └── launch.json ├── .gitignore ├── README.md ├── pubspec.yaml └── pubspec.lock /ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 803fcb6b326d64b0986b90f4303e6e8f -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /lib/src/styles/buttons.dart: -------------------------------------------------------------------------------- 1 | abstract class ButtonStyles { 2 | static double get buttonHeight => 50.0; 3 | } -------------------------------------------------------------------------------- /assets/images/beans.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/assets/images/beans.jpg -------------------------------------------------------------------------------- /assets/images/market.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/assets/images/market.jpg -------------------------------------------------------------------------------- /assets/images/top_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/assets/images/top_bg.png -------------------------------------------------------------------------------- /assets/images/vegetables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/assets/images/vegetables.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhandrew/mobilefarmersmarket/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/widgets/shopping_bag.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ShoppingBag extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Container( 7 | 8 | ); 9 | } 10 | } -------------------------------------------------------------------------------- /lib/src/widgets/profile_customer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ProfileCustomer extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Container( 7 | 8 | ); 9 | } 10 | } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/app.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | void main() async { 6 | WidgetsFlutterBinding.ensureInitialized(); 7 | await Firebase.initializeApp(); 8 | runApp(App()); 9 | } 10 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/src/styles/tabbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | abstract class TabBarStyles { 5 | 6 | static Color get unselectedLabelColor => AppColors.lightgray; 7 | static Color get labelColor => AppColors.straw; 8 | static Color get indicatorColor => AppColors.straw; 9 | } -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /lib/src/models/application_user.dart: -------------------------------------------------------------------------------- 1 | class ApplicationUser { 2 | final String userId; 3 | final String email; 4 | 5 | ApplicationUser({this.email, this.userId}); 6 | 7 | Map toMap(){ 8 | return { 9 | 'userId': userId, 10 | 'email': email 11 | }; 12 | } 13 | 14 | ApplicationUser.fromFirestore(Map firestore) 15 | : userId = firestore['userId'], 16 | email = firestore['email']; 17 | } -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/blocs/customer_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/models/market.dart'; 2 | import 'package:farmers_market/src/models/product.dart'; 3 | import 'package:farmers_market/src/services/firestore_service.dart'; 4 | 5 | class CustomerBloc { 6 | final db = FirestoreService(); 7 | 8 | //Get 9 | Stream> get fetchUpcomingMarkets => db.fetchUpcomingMarkets(); 10 | Stream> get fetchAvailableProducts => db.fetchAvailableProducts(); 11 | 12 | dispose(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/julow/farmers_market/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.julow.farmers_market 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/widgets/orders.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'dart:io'; 5 | 6 | class Orders extends StatelessWidget{ 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | if (Platform.isIOS){ 11 | return CupertinoPageScaffold( 12 | child: pageBody(), 13 | ); 14 | } else { 15 | return Scaffold( 16 | body: pageBody(), 17 | ); 18 | } 19 | } 20 | 21 | Widget pageBody() { 22 | return Center(child: Text('Orders'),); 23 | } 24 | } -------------------------------------------------------------------------------- /lib/src/styles/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class AppColors { 4 | static Color get darkgray => const Color(0xFF4e5b60); 5 | 6 | static Color get lightgray => const Color(0xFFc8d6ef); 7 | 8 | static Color get darkblue => const Color(0xFF263a44); 9 | 10 | static Color get lightblue => const Color(0xFF48a1af); 11 | 12 | static Color get straw => const Color(0xFFe2a84b); 13 | 14 | static Color get red => const Color(0xFFee5253); 15 | 16 | static Color get green => const Color(0xFF3b7d02); 17 | 18 | static Color get facebook => const Color(0xFF3b5998); 19 | 20 | static Color get google => const Color(0xFF4285f4); 21 | } 22 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/src/models/vendor.dart: -------------------------------------------------------------------------------- 1 | class Vendor { 2 | final String vendorId; 3 | final String name; 4 | final String imageUrl; 5 | final String description; 6 | 7 | Vendor({this.imageUrl, this.name, this.vendorId, this.description}); 8 | 9 | Map toMap() { 10 | return { 11 | 'vendorId': vendorId, 12 | 'name': name, 13 | 'imageUrl': imageUrl, 14 | 'description': description 15 | }; 16 | } 17 | 18 | factory Vendor.fromFirestore(Map firestore){ 19 | if (firestore == null) return null; 20 | 21 | return Vendor( 22 | vendorId: firestore['vendorId'], 23 | name: firestore['name'], 24 | imageUrl: firestore ['imageUrl'], 25 | description: firestore['description'] 26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /lib/src/services/firebase_storage_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_storage/firebase_storage.dart'; 2 | import 'dart:io'; 3 | 4 | class FirebaseStorageService { 5 | 6 | final storage = FirebaseStorage.instance; 7 | 8 | Future uploadProductImage(File file, String fileName) async { 9 | var snapshot = await storage.ref() 10 | .child('productImages/$fileName') 11 | .putFile(file) 12 | .onComplete; 13 | 14 | return await snapshot.ref.getDownloadURL(); 15 | } 16 | 17 | Future uploadVendorImage(File file, String fileName) async { 18 | var snapshot = await storage.ref() 19 | .child('vendorImages/$fileName') 20 | .putFile(file) 21 | .onComplete; 22 | 23 | return await snapshot.ref.getDownloadURL(); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/models/location.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | class Location { 5 | final String name; 6 | final String address; 7 | final String city; 8 | final String state; 9 | final GeoPoint geo; 10 | final String placesId; 11 | 12 | Location({ 13 | @required this.name, 14 | @required this.address, 15 | @required this.city, 16 | @required this.state, 17 | this.geo, 18 | this.placesId 19 | }); 20 | 21 | Location.fromFirestore(Map firestore) 22 | : name = firestore['name'], 23 | address = firestore['address'], 24 | city = firestore['city'], 25 | state = firestore['state'], 26 | geo = firestore['geo'] ?? null, 27 | placesId = firestore['placesId'] ?? null; 28 | 29 | 30 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | android/app/google-services.json 39 | ios/Runner.xcodeproj/project.pbxproj 40 | ios/Runner.xcodeproj/project.pbxproj 41 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/src/models/market.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/models/location.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | class Market { 5 | final String title; 6 | final String dateBegin; 7 | final String dateEnd; 8 | final Location location; 9 | final bool acceptingOrders; 10 | final String marketId; 11 | 12 | Market ({ 13 | @required this.title, 14 | @required this.dateBegin, 15 | @required this.dateEnd, 16 | @required this.location, 17 | @required this.marketId, 18 | this.acceptingOrders = false 19 | }); 20 | 21 | Market.fromFirestore(Map firestore) 22 | : title = firestore['title'], 23 | dateBegin = firestore['dateBegin'], 24 | dateEnd = firestore['dateEnd'], 25 | location = Location.fromFirestore(firestore['location']), 26 | marketId = firestore['marketId'], 27 | acceptingOrders = firestore['acceptingOrders']; 28 | 29 | } -------------------------------------------------------------------------------- /lib/src/widgets/navbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:farmers_market/src/styles/text.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | abstract class AppNavbar { 7 | 8 | static CupertinoSliverNavigationBar cupertinoNavBar ({String title, @required BuildContext context}) { 9 | return CupertinoSliverNavigationBar( 10 | largeTitle: Text(title, style: TextStyles.navTitle), 11 | backgroundColor: Colors.transparent, 12 | border: null, 13 | leading: GestureDetector(child: Icon(CupertinoIcons.back, color: AppColors.straw),onTap: () => Navigator.of(context).pop(),) 14 | ); 15 | } 16 | 17 | static SliverAppBar materialNavBar({@required String title, bool pinned, TabBar tabBar}) { 18 | return SliverAppBar( 19 | title: Text(title, style: TextStyles.navTitleMaterial), 20 | backgroundColor: AppColors.darkblue, 21 | bottom: tabBar, 22 | floating: true, 23 | pinned: (pinned ==null) ? true : pinned, 24 | snap: true, 25 | ); 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /lib/src/widgets/sliver_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/widgets/navbar.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | abstract class AppSliverScaffold { 6 | static CupertinoPageScaffold cupertinoSliverScaffold( 7 | {String navTitle, Widget pageBody, BuildContext context}) { 8 | return CupertinoPageScaffold( 9 | child: NestedScrollView( 10 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { 11 | return [ 12 | AppNavbar.cupertinoNavBar(title: navTitle,context: context), 13 | ]; 14 | }, 15 | body: pageBody, 16 | )); 17 | } 18 | 19 | static Scaffold materialSliverScaffold( 20 | {String navTitle, Widget pageBody, BuildContext context}) { 21 | return Scaffold( 22 | body: NestedScrollView( 23 | headerSliverBuilder: 24 | (BuildContext context, bool innerBoxIsScrolled) { 25 | return [AppNavbar.materialNavBar(title: navTitle,pinned: false)]; 26 | }, 27 | body: pageBody)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/styles/base.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | abstract class BaseStyles{ 5 | 6 | static double get borderRadius=> 25.0; 7 | 8 | static double get borderWidth=> 2.0; 9 | 10 | static double get listFieldHorizontal => 25.0; 11 | 12 | static double get listFieldVertical => 8.0; 13 | 14 | static double get animationOffset => 2.0; 15 | 16 | 17 | 18 | static EdgeInsets get listPadding { 19 | return EdgeInsets.symmetric(horizontal: listFieldHorizontal, vertical: listFieldVertical); 20 | } 21 | 22 | static List get boxShadow { 23 | return [ 24 | BoxShadow( 25 | color: AppColors.darkgray.withOpacity(.5), 26 | offset: Offset(1.0, 2.0), 27 | blurRadius: 2.0, 28 | ) 29 | ]; 30 | } 31 | 32 | static List get boxShadowPressed { 33 | return [ 34 | BoxShadow( 35 | color: AppColors.darkgray.withOpacity(.5), 36 | offset: Offset(1.0, 1.0), 37 | blurRadius: 1.0, 38 | ) 39 | ]; 40 | } 41 | 42 | static Widget iconPrefix(IconData icon) { 43 | return Padding( 44 | padding: const EdgeInsets.only(left: 8.0), 45 | child: Icon(icon, size: 35.0, color: AppColors.lightblue), 46 | ); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /lib/src/widgets/vendor_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:farmers_market/src/widgets/orders.dart'; 3 | import 'package:farmers_market/src/widgets/products.dart'; 4 | import 'package:farmers_market/src/widgets/profile.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | 7 | abstract class VendorScaffold { 8 | static CupertinoTabScaffold get cupertinoTabScaffold { 9 | return CupertinoTabScaffold( 10 | tabBar: _cupertinoTabBar, 11 | tabBuilder: (context, index) { 12 | return _pageSelection(index); 13 | }, 14 | ); 15 | } 16 | 17 | static get _cupertinoTabBar { 18 | return CupertinoTabBar( 19 | backgroundColor: AppColors.darkblue, 20 | items: [ 21 | BottomNavigationBarItem( 22 | icon: Icon(CupertinoIcons.create), title: Text('Products')), 23 | BottomNavigationBarItem( 24 | icon: Icon(CupertinoIcons.shopping_cart), title: Text('Orders')), 25 | BottomNavigationBarItem( 26 | icon: Icon(CupertinoIcons.person), title: Text('Profile')), 27 | ], 28 | ); 29 | } 30 | 31 | static Widget _pageSelection(int index) { 32 | if (index == 0) { 33 | return Products(); 34 | } 35 | 36 | if (index == 1) { 37 | return Orders(); 38 | } 39 | 40 | return Profile(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 176096730439-lqmc4tefmon0f1i20upl4nci0nnfh587.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.176096730439-lqmc4tefmon0f1i20upl4nci0nnfh587 9 | ANDROID_CLIENT_ID 10 | 176096730439-jbrjftjukmud03mnkro156crhoog77qf.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyD_hOduvwxmlNaOqi9cJ6U_E6pFbPLOoAY 13 | GCM_SENDER_ID 14 | 176096730439 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | com.julow.farmersMarket 19 | PROJECT_ID 20 | farmers-market-89c2a 21 | STORAGE_BUCKET 22 | farmers-market-89c2a.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:176096730439:ios:f48cf4c8ebd008fe720062 35 | DATABASE_URL 36 | https://farmers-market-89c2a.firebaseio.com 37 | 38 | -------------------------------------------------------------------------------- /lib/src/widgets/customer_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:farmers_market/src/widgets/products_customer.dart'; 3 | import 'package:farmers_market/src/widgets/profile_customer.dart'; 4 | import 'package:farmers_market/src/widgets/shopping_bag.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 7 | 8 | abstract class CustomerScaffold { 9 | static CupertinoTabScaffold get cupertinoTabScaffold { 10 | return CupertinoTabScaffold( 11 | tabBar: _cupertinoTabBar, 12 | tabBuilder: (context, index) { 13 | return _pageSelection(index); 14 | }, 15 | ); 16 | } 17 | 18 | static get _cupertinoTabBar { 19 | return CupertinoTabBar( 20 | backgroundColor: AppColors.darkblue, 21 | items: [ 22 | BottomNavigationBarItem( 23 | icon: Icon(CupertinoIcons.create), title: Text('Products')), 24 | BottomNavigationBarItem( 25 | icon: Icon(FontAwesomeIcons.shoppingBag), title: Text('Orders')), 26 | BottomNavigationBarItem( 27 | icon: Icon(CupertinoIcons.person), title: Text('Profile')), 28 | ], 29 | ); 30 | } 31 | 32 | static Widget _pageSelection(int index) { 33 | if (index == 0) { 34 | return ProductsCustomer(); 35 | } 36 | 37 | if (index == 1) { 38 | return ShoppingBag(); 39 | } 40 | 41 | return ProfileCustomer(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/widgets/profile.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'dart:io'; 5 | 6 | import 'package:provider/provider.dart'; 7 | 8 | class Profile extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | if (Platform.isIOS) { 12 | return CupertinoPageScaffold( 13 | child: pageBody(context), 14 | ); 15 | } else { 16 | return Scaffold( 17 | body: pageBody(context), 18 | ); 19 | } 20 | } 21 | 22 | Widget pageBody(BuildContext context) { 23 | var authBloc = Provider.of(context); 24 | // return Center( 25 | // child: (Platform.isIOS) 26 | // ? CupertinoButton( 27 | // child: Text('Logout'), 28 | // onPressed: () => authBloc.logout(), 29 | // ) 30 | // : FlatButton( 31 | // child: Text('Logout'), 32 | // onPressed: () => authBloc.logout(), 33 | // )); 34 | 35 | return Center( 36 | child: (Platform.isIOS) 37 | ? CupertinoButton( 38 | child: Text('Add'), 39 | onPressed: () => Navigator.of(context).pushNamed('/editvendor') , 40 | ) 41 | : FlatButton( 42 | child: Text('Add'), 43 | onPressed: () => Navigator.of(context).pushNamed('/editvendor'), 44 | )); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/models/product.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class Product{ 4 | final String productName; 5 | final String unitType; 6 | final double unitPrice; 7 | final int availableUnits; 8 | final String vendorId; 9 | final String productId; 10 | final String imageUrl; 11 | final bool approved; 12 | final String note; 13 | 14 | Product({ 15 | @required this.approved, 16 | @required this.availableUnits, 17 | this.imageUrl = "", 18 | this.note = "", 19 | @required this.productId, 20 | @required this.productName, 21 | @required this.unitPrice, 22 | @required this.unitType, 23 | @required this.vendorId 24 | }); 25 | 26 | Map toMap() { 27 | return { 28 | 'productName' : productName, 29 | 'unitType' : unitType, 30 | 'unitPrice' : unitPrice, 31 | 'availableUnits': availableUnits, 32 | 'approved': approved, 33 | 'imageUrl':imageUrl, 34 | 'note':note, 35 | 'productId':productId, 36 | 'vendorId':vendorId 37 | }; 38 | } 39 | 40 | Product.fromFirestore(Map firestore) 41 | : productName = firestore['productName'], 42 | unitType = firestore['unitType'], 43 | unitPrice = firestore['unitPrice'], 44 | availableUnits = firestore['availableUnits'], 45 | approved = firestore['approved'], 46 | imageUrl = firestore['imageUrl'], 47 | note = firestore['note'], 48 | productId = firestore['productId'], 49 | vendorId = firestore['vendorId']; 50 | } -------------------------------------------------------------------------------- /lib/src/widgets/social_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/buttons.dart'; 3 | import 'package:farmers_market/src/styles/colors.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 6 | 7 | class AppSocialButton extends StatelessWidget { 8 | final SocialType socialType; 9 | 10 | AppSocialButton({@required this.socialType}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | Color buttonColor; 15 | Color iconColor; 16 | IconData icon; 17 | 18 | switch (socialType) { 19 | case SocialType.Facebook: 20 | iconColor = Colors.white; 21 | buttonColor = AppColors.facebook; 22 | icon = FontAwesomeIcons.facebookF; 23 | break; 24 | case SocialType.Google: 25 | iconColor = Colors.white; 26 | buttonColor = AppColors.google; 27 | icon = FontAwesomeIcons.google; 28 | break; 29 | default: 30 | iconColor = Colors.white; 31 | buttonColor = AppColors.facebook; 32 | icon = FontAwesomeIcons.facebookF; 33 | break; 34 | } 35 | 36 | return Container( 37 | height: ButtonStyles.buttonHeight, 38 | width: ButtonStyles.buttonHeight, 39 | decoration: BoxDecoration( 40 | color: buttonColor, 41 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius), 42 | boxShadow: BaseStyles.boxShadow), 43 | child: Icon(icon, color: iconColor), 44 | ); 45 | } 46 | } 47 | 48 | enum SocialType { Facebook, Google } 49 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/src/widgets/alerts.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/text.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | abstract class AppAlerts { 6 | 7 | static Future showErrorDialog(bool isIOS,BuildContext context, String errorMessage) async { 8 | 9 | return (isIOS) 10 | ? showCupertinoDialog( 11 | context: context, 12 | builder: (context){ 13 | return CupertinoAlertDialog( 14 | title: Text('Error',style: TextStyles.subtitle,), 15 | content: SingleChildScrollView( 16 | child: ListBody( 17 | children: [ 18 | Text(errorMessage, style: TextStyles.body) 19 | ], 20 | ), 21 | ), 22 | actions: [ 23 | CupertinoButton( 24 | child: Text('Okay',style: TextStyles.body), 25 | onPressed: () => Navigator.of(context).pop(), 26 | ) 27 | ], 28 | ); 29 | } 30 | ) 31 | : showDialog( 32 | context: context, 33 | barrierDismissible: false, 34 | builder: (context){ 35 | return AlertDialog( 36 | title: Text('Error',style: TextStyles.subtitle,), 37 | content: SingleChildScrollView( 38 | child: ListBody( 39 | children: [ 40 | Text(errorMessage, style: TextStyles.body) 41 | ], 42 | ), 43 | ), 44 | actions: [ 45 | FlatButton( 46 | child: Text('Okay',style: TextStyles.body), 47 | onPressed: () => Navigator.of(context).pop(), 48 | ) 49 | ], 50 | ); 51 | } 52 | ); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | farmers_market 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | NSPhotoLibraryUsageDescription 45 | Permission to access photos for upload 46 | 47 | 48 | -------------------------------------------------------------------------------- /lib/src/widgets/list_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/colors.dart'; 3 | import 'package:farmers_market/src/styles/text.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 6 | 7 | class AppListTile extends StatelessWidget { 8 | final String month; 9 | final String date; 10 | final String title; 11 | final String location; 12 | final bool acceptingOrders; 13 | final String marketId; 14 | 15 | AppListTile({ 16 | @required this.month, 17 | @required this.date, 18 | @required this.title, 19 | @required this.location, 20 | @required this.marketId, 21 | this.acceptingOrders = false 22 | }); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Column( 27 | children: [ 28 | ListTile( 29 | leading: CircleAvatar( 30 | radius: 25.0, 31 | backgroundColor: AppColors.lightblue, 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | children: [ 35 | Text(month,style: TextStyle(color:Colors.white, fontSize: 12.0),), 36 | Text(date, style: TextStyles.buttonTextLight) 37 | ],), 38 | ), 39 | title: Text(title,style:TextStyles.subtitle), 40 | subtitle: Text(location), 41 | trailing:(acceptingOrders) ? Icon(FontAwesomeIcons.shoppingBasket,color: AppColors.darkblue,): Text(''), 42 | onTap: (acceptingOrders) ? () => Navigator.of(context).pushNamed('/customer/$marketId'): null, 43 | ), 44 | Padding( 45 | padding: EdgeInsets.symmetric(horizontal: BaseStyles.listFieldHorizontal), 46 | child: Divider(color:AppColors.lightgray), 47 | ) 48 | ], 49 | ); 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply plugin: 'com.google.gms.google-services' // Google Services plugin 27 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 28 | 29 | android { 30 | compileSdkVersion 28 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | applicationId "com.julow.farmers_market" 42 | minSdkVersion 21 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.1' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 67 | } 68 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Farmer's Market Mobile Ordering Application 2 | 3 | This application will be similar to the Mobile Ordering system introduced by Walt Disney World where customers may place their order online and confirm their proximity to the market via location of the device. The vendor can confirm their order accept payment by Stripe and let the customer know where they can pickup the order. If desired, the vendor can confirm the customer by flashing the flash on their device. The entire transaction can take place without any personal contact. 4 | 5 | This will be an open source project and anyone is welcome to fork, clone and use the code for their own purposes. The application is meant to be of public use and the development is intended for educational purposes for Flutter developers. Each episode in the series will be a branch in the repository. Episode 1 contains no coding and does not have a branch 6 | 7 | Concepts introduced in the development of the application will include: Firebase, Firestore, 3rd Party Authentication, push notifications, provider, rxdart, cupertino scaffolding, plugins, geocoding, Strip, and Google Maps (and maybe more). 8 | 9 | Development of the application will be documented on my YouTube channel https://www.youtube.com/channel/UCSKeK_8IzsqwKQBJuIGJPaA 10 | 11 | ## Getting Started 12 | To run the code beyond part 5 you will need to establish a firebase project, add an IOS and Android application using the Flutter instructions here https://firebase.google.com/docs/flutter/setup?authuser=0&platform=ios 13 | 14 | To run beyond part 18, add a Firestore database to your project with rules set to allow read, write: if request.auth.uid != null; 15 | You will also need to enable email authentication in your firebase project. 16 | 17 | 18 | ## Episode History 19 | 20 | ### Part 1: Application Concept (No Branch) 21 | ### Part 2: App Setup and Routes 22 | ### Part 3: App Platform Splitting 23 | ### Part 4: Adding Firebase 24 | ### Part 5: Login Flow (No Branch) 25 | ### Part 6: Login UI 26 | ### Part 7: Cupertino TextField (in Part8 branch) 27 | ### Part 8: Material TextField 28 | ### Part 9: Application TextField 29 | ### Part 10: Application Buttons 30 | ### Part 11: Social Media Buttons 31 | ### Part 12: Signup UI 32 | ### Part 13: Why RXDart? (no branch) 33 | ### Part 14: TheAuth BLoC 34 | ### Part 15: Material Validation 35 | ### Part 16: Cupertino Validation 36 | ### Part 17: Submit Button Revisited 37 | ### Part 18: Adding the First User 38 | ### Part 19: User Story Mapping 39 | ### Part 20: Logging In 40 | ### Part 21: IOS Sliver AppBar 41 | 42 | -------------------------------------------------------------------------------- /lib/src/widgets/card.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/colors.dart'; 3 | import 'package:farmers_market/src/styles/text.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:intl/intl.dart'; 6 | 7 | class AppCard extends StatelessWidget { 8 | final String productName; 9 | final String unitType; 10 | final int availableUnits; 11 | final double price; 12 | final String note; 13 | final String imageUrl; 14 | 15 | final formatCurrency = NumberFormat.simpleCurrency(); 16 | 17 | AppCard({ 18 | @required this.productName, 19 | @required this.unitType, 20 | @required this.availableUnits, 21 | @required this.price, 22 | this.imageUrl, 23 | this.note = "", 24 | }); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Container( 29 | margin: BaseStyles.listPadding, 30 | padding: BaseStyles.listPadding, 31 | decoration: BoxDecoration( 32 | boxShadow: BaseStyles.boxShadow, 33 | color: Colors.white, 34 | border: Border.all( 35 | color: AppColors.darkblue, 36 | width: BaseStyles.borderWidth, 37 | ), 38 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius) 39 | ), 40 | child: Column( 41 | children: [ 42 | Row( 43 | children: [ 44 | Padding( 45 | padding: const EdgeInsets.only(right: 10.0, bottom: 10.0, top: 10.0), 46 | child: (imageUrl != null && imageUrl != "") 47 | ? ClipRRect(child: Image.network(imageUrl, height: 100.0,) 48 | ,borderRadius: BorderRadius.circular(5.0),) 49 | : Image.asset('assets/images/vegetables.png', height: 100.0,), 50 | child: Image.asset('assets/images/vegetables.png', height: 100.0,), 51 | ), 52 | Column( 53 | crossAxisAlignment: CrossAxisAlignment.start, 54 | children: [ 55 | Text(productName,style: TextStyles.subtitle), 56 | Text('${formatCurrency.format(price)}/$unitType', style: TextStyles.body), 57 | (availableUnits > 0 ) 58 | ? Text('In Stock', style: TextStyles.bodyLightBlue) 59 | : Text('Currently Unavailable',style: TextStyles.bodyRed) 60 | ],) 61 | ], 62 | ), 63 | Text(note, style: TextStyles.body) 64 | ], 65 | ), 66 | ); 67 | } 68 | } -------------------------------------------------------------------------------- /lib/src/styles/textfields.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/colors.dart'; 3 | import 'package:farmers_market/src/styles/text.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | abstract class TextFieldStyles { 7 | static double get textBoxHorizontal => BaseStyles.listFieldHorizontal; 8 | 9 | static double get textBoxVertical => BaseStyles.listFieldVertical; 10 | 11 | static TextStyle get text => TextStyles.body; 12 | 13 | static TextStyle get placeholder => TextStyles.suggestion; 14 | 15 | static Color get cursorColor => AppColors.darkblue; 16 | 17 | static Widget iconPrefix(IconData icon) => BaseStyles.iconPrefix(icon); 18 | 19 | static TextAlign get textAlign => TextAlign.center; 20 | 21 | static BoxDecoration get cupertinoDecoration { 22 | return BoxDecoration( 23 | border: Border.all( 24 | color: AppColors.straw, 25 | width: BaseStyles.borderWidth, 26 | ), 27 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)); 28 | } 29 | 30 | static BoxDecoration get cupertinoErrorDecoration { 31 | return BoxDecoration( 32 | border: Border.all( 33 | color: AppColors.red, 34 | width: BaseStyles.borderWidth, 35 | ), 36 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)); 37 | } 38 | 39 | static InputDecoration materialDecoration( 40 | String hintText, IconData icon, String errorText) { 41 | return InputDecoration( 42 | contentPadding: EdgeInsets.all(8.0), 43 | hintText: hintText, 44 | hintStyle: TextFieldStyles.placeholder, 45 | border: InputBorder.none, 46 | errorText: errorText, 47 | errorStyle: TextStyles.error, 48 | focusedBorder: OutlineInputBorder( 49 | borderSide: 50 | BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), 51 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), 52 | enabledBorder: OutlineInputBorder( 53 | borderSide: 54 | BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), 55 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), 56 | focusedErrorBorder: OutlineInputBorder( 57 | borderSide: 58 | BorderSide(color: AppColors.straw, width: BaseStyles.borderWidth), 59 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), 60 | errorBorder: OutlineInputBorder( 61 | borderSide: 62 | BorderSide(color: AppColors.red, width: BaseStyles.borderWidth), 63 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius)), 64 | prefixIcon: iconPrefix(icon), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/screens/vendor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 4 | import 'package:farmers_market/src/styles/tabbar.dart'; 5 | import 'package:farmers_market/src/widgets/navbar.dart'; 6 | import 'package:farmers_market/src/widgets/orders.dart'; 7 | import 'package:farmers_market/src/widgets/products.dart'; 8 | import 'package:farmers_market/src/widgets/profile.dart'; 9 | import 'package:farmers_market/src/widgets/vendor_scaffold.dart'; 10 | import 'package:flutter/cupertino.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'dart:io'; 13 | 14 | import 'package:provider/provider.dart'; 15 | 16 | class Vendor extends StatefulWidget { 17 | 18 | 19 | @override 20 | _VendorState createState() => _VendorState(); 21 | 22 | static TabBar get vendorTabBar { 23 | return TabBar( 24 | unselectedLabelColor: TabBarStyles.unselectedLabelColor , 25 | labelColor: TabBarStyles.labelColor , 26 | indicatorColor: TabBarStyles.indicatorColor , 27 | tabs: [ 28 | Tab(icon: Icon(Icons.list)), 29 | Tab(icon: Icon(Icons.shopping_cart)), 30 | Tab(icon: Icon(Icons.person)), 31 | ], 32 | ); 33 | } 34 | } 35 | 36 | class _VendorState extends State { 37 | StreamSubscription _userSubscription; 38 | 39 | @override 40 | void initState() { 41 | Future.delayed(Duration.zero, (){ 42 | var authBloc = Provider.of(context,listen: false); 43 | _userSubscription = authBloc.user.listen((user) { 44 | if (user == null) Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => false); 45 | }); 46 | }); 47 | 48 | super.initState(); 49 | } 50 | 51 | @override 52 | void dispose() { 53 | _userSubscription.cancel(); 54 | super.dispose(); 55 | } 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | 60 | if (Platform.isIOS) { 61 | return CupertinoPageScaffold( 62 | child: NestedScrollView( 63 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){ 64 | return [ 65 | AppNavbar.cupertinoNavBar(title: 'Vendor Name',context: context), 66 | ]; 67 | }, 68 | body: VendorScaffold.cupertinoTabScaffold, 69 | ), 70 | ); 71 | } else { 72 | return DefaultTabController( 73 | length: 3, 74 | child: Scaffold( 75 | body: NestedScrollView( 76 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){ 77 | return [ 78 | AppNavbar.materialNavBar(title: 'Vendor Name', tabBar: Vendor.vendorTabBar) 79 | ]; 80 | }, 81 | body: TabBarView(children: [ 82 | Products(), 83 | Orders(), 84 | Profile(), 85 | ],) 86 | ) 87 | ), 88 | ); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /lib/src/styles/text.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | abstract class TextStyles { 6 | static TextStyle get title { 7 | return GoogleFonts.poppins( 8 | textStyle: TextStyle( 9 | color: AppColors.darkblue, 10 | fontWeight: FontWeight.bold, 11 | fontSize: 40.0)); 12 | } 13 | 14 | static TextStyle get subtitle { 15 | return GoogleFonts.economica( 16 | textStyle: TextStyle( 17 | color: AppColors.straw, 18 | fontWeight: FontWeight.bold, 19 | fontSize: 30.0)); 20 | } 21 | 22 | static TextStyle get listTitle { 23 | return GoogleFonts.economica( 24 | textStyle: TextStyle( 25 | color: AppColors.straw, 26 | fontWeight: FontWeight.bold, 27 | fontSize: 25.0)); 28 | } 29 | 30 | static TextStyle get navTitle { 31 | return GoogleFonts.poppins( 32 | textStyle: 33 | TextStyle(color: AppColors.darkblue, fontWeight: FontWeight.bold)); 34 | } 35 | 36 | static TextStyle get navTitleMaterial { 37 | return GoogleFonts.poppins( 38 | textStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)); 39 | } 40 | 41 | static TextStyle get body { 42 | return GoogleFonts.roboto( 43 | textStyle: TextStyle(color: AppColors.darkgray, fontSize: 16.0)); 44 | } 45 | 46 | static TextStyle get bodyLightBlue { 47 | return GoogleFonts.roboto( 48 | textStyle: TextStyle(color: AppColors.lightblue, fontSize: 16.0)); 49 | } 50 | 51 | static TextStyle get bodyRed { 52 | return GoogleFonts.roboto( 53 | textStyle: TextStyle(color: AppColors.red, fontSize: 16.0)); 54 | } 55 | 56 | static TextStyle get picker { 57 | return GoogleFonts.roboto( 58 | textStyle: TextStyle(color: AppColors.darkgray, fontSize: 35.0)); 59 | } 60 | 61 | 62 | static TextStyle get link { 63 | return GoogleFonts.roboto( 64 | textStyle: TextStyle( 65 | color: AppColors.straw, 66 | fontSize: 16.0, 67 | fontWeight: FontWeight.bold)); 68 | } 69 | 70 | static TextStyle get suggestion { 71 | return GoogleFonts.roboto( 72 | textStyle: TextStyle(color: AppColors.lightgray, fontSize: 14.0)); 73 | } 74 | 75 | static TextStyle get error { 76 | return GoogleFonts.roboto( 77 | textStyle: TextStyle(color: AppColors.red, fontSize: 12.0)); 78 | } 79 | 80 | static TextStyle get buttonTextLight { 81 | return GoogleFonts.roboto( 82 | textStyle: TextStyle( 83 | color: Colors.white, fontSize: 17.0, fontWeight: FontWeight.bold)); 84 | } 85 | 86 | static TextStyle get buttonTextDark { 87 | return GoogleFonts.roboto( 88 | textStyle: TextStyle( 89 | color: AppColors.darkgray, 90 | fontSize: 17.0, 91 | fontWeight: FontWeight.bold)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/src/widgets/products_customer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:farmers_market/src/blocs/customer_bloc.dart'; 4 | import 'package:farmers_market/src/models/product.dart'; 5 | import 'package:farmers_market/src/styles/colors.dart'; 6 | import 'package:farmers_market/src/styles/text.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:intl/intl.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | class ProductsCustomer extends StatelessWidget { 13 | final formatCurrency = NumberFormat.simpleCurrency(); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | var customerBloc = Provider.of(context); 18 | 19 | return StreamBuilder>( 20 | stream: customerBloc.fetchAvailableProducts, 21 | builder: (context, snapshot) { 22 | if (!snapshot.hasData) 23 | return Center( 24 | child: (Platform.isIOS) 25 | ? CupertinoActivityIndicator() 26 | : CircularProgressIndicator(), 27 | ); 28 | 29 | return Scaffold( 30 | body: Column( 31 | children: [ 32 | Expanded( 33 | child: ListView.builder( 34 | itemCount: snapshot.data.length, 35 | itemBuilder: (context, index) { 36 | var product = snapshot.data[index]; 37 | 38 | return Column( 39 | children: [ 40 | ListTile( 41 | leading: CircleAvatar( 42 | backgroundImage: (product.imageUrl != '') 43 | ? NetworkImage(product.imageUrl) 44 | : AssetImage('assets/images/vegetables.png'), 45 | radius: 25.0, 46 | ), 47 | title: Text(product.productName,style:TextStyles.listTitle), 48 | subtitle: Text('The Vendor'), 49 | trailing: Text('${formatCurrency.format(product.unitPrice)}/${product.unitType}',style:TextStyles.bodyLightBlue), 50 | ), 51 | Divider(color: AppColors.lightgray,) 52 | ], 53 | ); 54 | }), 55 | ), 56 | Container( 57 | height: 50.0, 58 | width: double.infinity, 59 | color: AppColors.straw, 60 | child: (Platform.isIOS) 61 | ? Icon(IconData(0xF38B,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage),color:Colors.white, size:35.0) 62 | : Icon(Icons.filter_list,color:Colors.white, size:35.0) , 63 | ) 64 | ], 65 | ), 66 | ); 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/services/firestore_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:farmers_market/src/models/application_user.dart'; 3 | import 'package:farmers_market/src/models/market.dart'; 4 | import 'package:farmers_market/src/models/product.dart'; 5 | import 'package:farmers_market/src/models/vendor.dart'; 6 | 7 | 8 | class FirestoreService { 9 | FirebaseFirestore _db = FirebaseFirestore.instance; 10 | 11 | Future addUser(ApplicationUser user) { 12 | return _db.collection('users').doc(user.userId).set(user.toMap()); 13 | } 14 | 15 | Future fetchUser(String userId) { 16 | return _db 17 | .collection('users') 18 | .doc(userId) 19 | .get() 20 | .then((snapshot) => ApplicationUser.fromFirestore(snapshot.data())); 21 | } 22 | 23 | Stream> fetchUnitTypes() { 24 | return _db.collection('types').doc('units').snapshots().map( 25 | (snapshot) => snapshot.data()['production'] 26 | .map((type) => type.toString()) 27 | .toList()); 28 | } 29 | 30 | Future setProduct(Product product) { 31 | var options = SetOptions(merge:true); 32 | return _db 33 | .collection('products') 34 | .doc(product.productId) 35 | .set(product.toMap(),options); 36 | } 37 | 38 | Future fetchProduct(String productId){ 39 | return _db.collection('products').doc(productId) 40 | .get().then((snapshot) => Product.fromFirestore(snapshot.data())); 41 | } 42 | 43 | Stream> fetchProductsByVendorId(String vendorId) { 44 | return _db 45 | .collection('products') 46 | .where('vendorId', isEqualTo: vendorId) 47 | .snapshots() 48 | .map((query) => query.docs) 49 | .map((snapshot) => 50 | snapshot.map((doc) => Product.fromFirestore(doc.data())) 51 | .toList()); 52 | } 53 | 54 | Stream> fetchUpcomingMarkets(){ 55 | return _db 56 | .collection('markets') 57 | .where('dateEnd', isGreaterThan: DateTime.now().toIso8601String()) 58 | .snapshots() 59 | .map((query) => query.docs) 60 | .map((snapshot) => snapshot 61 | .map((doc) => Market.fromFirestore(doc.data())) 62 | .toList()); 63 | } 64 | 65 | Stream> fetchAvailableProducts(){ 66 | return _db 67 | .collection('products') 68 | .where('availableUnits', isGreaterThan: 0) 69 | .snapshots() 70 | .map((query) => query.docs) 71 | .map((snapshot) => snapshot.map((doc) => Product.fromFirestore(doc.data())) 72 | .toList()); 73 | } 74 | 75 | Future fetchVendor(String vendorId){ 76 | return _db 77 | .collection('vendors') 78 | .doc(vendorId) 79 | .get().then((snapshot) => Vendor.fromFirestore(snapshot.data())); 80 | } 81 | 82 | Future setVendor(Vendor vendor){ 83 | var options = SetOptions(merge:true); 84 | 85 | return _db 86 | .collection('vendors') 87 | .doc(vendor.vendorId) 88 | .set(vendor.toMap(),options); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /lib/src/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 2 | import 'package:farmers_market/src/blocs/customer_bloc.dart'; 3 | import 'package:farmers_market/src/blocs/product_bloc.dart'; 4 | import 'package:farmers_market/src/routes.dart'; 5 | import 'package:farmers_market/src/screens/landing.dart'; 6 | import 'package:farmers_market/src/screens/login.dart'; 7 | import 'package:farmers_market/src/services/firestore_service.dart'; 8 | import 'package:farmers_market/src/styles/colors.dart'; 9 | import 'package:farmers_market/src/styles/text.dart'; 10 | import 'package:flutter/cupertino.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'dart:io'; 13 | 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'blocs/vendor_bloc.dart'; 17 | final authBloc = AuthBloc(); 18 | final productBloc = ProductBloc(); 19 | final customerBloc = CustomerBloc(); 20 | final vendorBloc = VendorBloc(); 21 | final firestoreService = FirestoreService(); 22 | 23 | class App extends StatefulWidget { 24 | @override 25 | _AppState createState() => _AppState(); 26 | } 27 | 28 | class _AppState extends State { 29 | @override 30 | Widget build(BuildContext context) { 31 | return MultiProvider( 32 | providers: [ 33 | Provider(create: (context) => authBloc), 34 | Provider(create: (context) => productBloc), 35 | Provider(create: (context) => customerBloc,), 36 | Provider(create: (context) => vendorBloc,), 37 | FutureProvider(create: (context) => authBloc.isLoggedIn()), 38 | StreamProvider(create: (context) => firestoreService.fetchUnitTypes()) 39 | ], 40 | child: PlatformApp()); 41 | } 42 | 43 | @override 44 | void dispose() { 45 | authBloc.dispose(); 46 | productBloc.dispose(); 47 | customerBloc.dispose(); 48 | vendorBloc.dispose(); 49 | super.dispose(); 50 | } 51 | } 52 | 53 | class PlatformApp extends StatelessWidget { 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | 58 | var isLoggedIn = Provider.of(context); 59 | 60 | if (Platform.isIOS) { 61 | return CupertinoApp( 62 | home: (isLoggedIn == null) ? loadingScreen(true) : (isLoggedIn == true ) ? Landing() : Login(), 63 | onGenerateRoute: Routes.cupertinoRoutes, 64 | theme: CupertinoThemeData( 65 | primaryColor: AppColors.straw, 66 | scaffoldBackgroundColor: Colors.white, 67 | textTheme: CupertinoTextThemeData( 68 | tabLabelTextStyle: TextStyles.suggestion 69 | ) 70 | ) 71 | ); 72 | } else { 73 | return MaterialApp( 74 | home: (isLoggedIn == null) ? loadingScreen(false) : (isLoggedIn == true ) ? Landing() : Login(), 75 | onGenerateRoute: Routes.materialRoutes, 76 | theme: ThemeData(scaffoldBackgroundColor: Colors.white) 77 | ); 78 | } 79 | } 80 | 81 | Widget loadingScreen(bool isIOS){ 82 | return (isIOS) 83 | ? CupertinoPageScaffold(child: Center(child: CupertinoActivityIndicator(),),) 84 | : Scaffold(body: Center(child: CircularProgressIndicator())); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/src/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/screens/customer.dart'; 2 | import 'package:farmers_market/src/screens/edit_product.dart'; 3 | import 'package:farmers_market/src/screens/edit_vendor.dart'; 4 | import 'package:farmers_market/src/screens/landing.dart'; 5 | import 'package:farmers_market/src/screens/login.dart'; 6 | import 'package:farmers_market/src/screens/signup.dart'; 7 | import 'package:farmers_market/src/screens/vendor.dart'; 8 | import 'package:flutter/cupertino.dart'; 9 | import 'package:flutter/material.dart'; 10 | 11 | abstract class Routes { 12 | static MaterialPageRoute materialRoutes(RouteSettings settings) { 13 | switch (settings.name) { 14 | case "/landing": 15 | return MaterialPageRoute(builder: (context) => Landing()); 16 | case "/signup": 17 | return MaterialPageRoute(builder: (context) => Signup()); 18 | case "/login": 19 | return MaterialPageRoute(builder: (context) => Login()); 20 | case "/vendor": 21 | return MaterialPageRoute(builder: (context) => Vendor()); 22 | case "/editproduct": 23 | return MaterialPageRoute(builder: (context) => EditProduct()); 24 | case "/editvendor": 25 | return MaterialPageRoute(builder: (context) => EditVendor()); 26 | default: 27 | var routeArray = settings.name.split('/'); 28 | if (settings.name.contains('/editproduct/')) { 29 | return MaterialPageRoute( 30 | builder: (context) => EditProduct( 31 | productId: routeArray[2], 32 | )); 33 | } else if (settings.name.contains('/customer/')) { 34 | return MaterialPageRoute( 35 | builder: (context) => Customer( 36 | marketId: routeArray[2], 37 | )); 38 | } 39 | return MaterialPageRoute(builder: (context) => Login()); 40 | } 41 | } 42 | 43 | static CupertinoPageRoute cupertinoRoutes(RouteSettings settings) { 44 | switch (settings.name) { 45 | case "/landing": 46 | return CupertinoPageRoute(builder: (context) => Landing()); 47 | case "/signup": 48 | return CupertinoPageRoute(builder: (context) => Signup()); 49 | case "/login": 50 | return CupertinoPageRoute(builder: (context) => Login()); 51 | case "/vendor": 52 | return CupertinoPageRoute(builder: (context) => Vendor()); 53 | case "/editproduct": 54 | return CupertinoPageRoute(builder: (context) => EditProduct()); 55 | case "/editvendor": 56 | return CupertinoPageRoute(builder: (context) => EditVendor()); 57 | default: 58 | var routeArray = settings.name.split('/'); 59 | if (settings.name.contains('/editproduct/')) { 60 | return CupertinoPageRoute( 61 | builder: (context) => EditProduct( 62 | productId: routeArray[2], 63 | )); 64 | } else if (settings.name.contains('/customer/')) { 65 | return CupertinoPageRoute( 66 | builder: (context) => Customer( 67 | marketId: routeArray[2], 68 | )); 69 | } 70 | return CupertinoPageRoute(builder: (context) => Login()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: farmers_market 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | firebase_core: ^0.5.0 27 | firebase_auth: ^0.18.0+1 28 | cloud_firestore: ^0.14.0+2 29 | google_fonts: ^1.0.0 30 | font_awesome_flutter: ^8.8.1 31 | rxdart: ^0.24.1 32 | provider: ^4.3.2+2 33 | fluttertoast: ^4.0.1 34 | uuid: ^2.0.4 35 | intl: ^0.16.1 36 | image_picker: ^0.6.7+3 37 | firebase_storage: ^4.0.0 38 | permission_handler: ^5.0.1+1 39 | flutter_native_image: ^0.0.5+2 40 | date_format: ^1.0.8 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | 46 | 47 | # For information on the generic Dart part of this file, see the 48 | # following page: https://dart.dev/tools/pub/pubspec 49 | 50 | # The following section is specific to Flutter. 51 | flutter: 52 | 53 | # The following line ensures that the Material Icons font is 54 | # included with your application, so that you can use the icons in 55 | # the material Icons class. 56 | uses-material-design: true 57 | 58 | # To add assets to your application, add an assets section, like this: 59 | assets: 60 | - assets/images/ 61 | # - images/a_dot_ham.jpeg 62 | 63 | # An image asset can refer to one or more resolution-specific "variants", see 64 | # https://flutter.dev/assets-and-images/#resolution-aware. 65 | 66 | # For details regarding adding assets from package dependencies, see 67 | # https://flutter.dev/assets-and-images/#from-packages 68 | 69 | # To add custom fonts to your application, add a fonts section here, 70 | # in this "flutter" section. Each entry in this list should have a 71 | # "family" key with the font family name, and a "fonts" key with a 72 | # list giving the asset and other descriptors for the font. For 73 | # example: 74 | # fonts: 75 | # - family: Schyler 76 | # fonts: 77 | # - asset: fonts/Schyler-Regular.ttf 78 | # - asset: fonts/Schyler-Italic.ttf 79 | # style: italic 80 | # - family: Trajan Pro 81 | # fonts: 82 | # - asset: fonts/TrajanPro.ttf 83 | # - asset: fonts/TrajanPro_Bold.ttf 84 | # weight: 700 85 | # 86 | # For details regarding fonts from package dependencies, 87 | # see https://flutter.dev/custom-fonts/#from-packages 88 | -------------------------------------------------------------------------------- /lib/src/widgets/button.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/buttons.dart'; 3 | import 'package:farmers_market/src/styles/colors.dart'; 4 | import 'package:farmers_market/src/styles/text.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class AppButton extends StatefulWidget{ 8 | final String buttonText; 9 | final ButtonType buttonType; 10 | final void Function() onPressed; 11 | 12 | AppButton({ 13 | @required this.buttonText, 14 | this.buttonType, 15 | this.onPressed 16 | }); 17 | 18 | @override 19 | _AppButtonState createState() => _AppButtonState(); 20 | } 21 | 22 | class _AppButtonState extends State { 23 | bool pressed = false; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | TextStyle fontStyle; 28 | Color buttonColor; 29 | 30 | switch (widget.buttonType) { 31 | case ButtonType.Straw: 32 | fontStyle = TextStyles.buttonTextLight; 33 | buttonColor = AppColors.straw; 34 | break; 35 | case ButtonType.LightBlue: 36 | fontStyle = TextStyles.buttonTextLight; 37 | buttonColor = AppColors.lightblue; 38 | break; 39 | case ButtonType.DarkBlue: 40 | fontStyle = TextStyles.buttonTextLight; 41 | buttonColor = AppColors.darkblue; 42 | break; 43 | case ButtonType.Disabled: 44 | fontStyle = TextStyles.buttonTextLight; 45 | buttonColor = AppColors.lightgray; 46 | break; 47 | case ButtonType.DarkGray: 48 | fontStyle = TextStyles.buttonTextLight; 49 | buttonColor = AppColors.darkgray; 50 | break; 51 | default: 52 | fontStyle = TextStyles.buttonTextLight; 53 | buttonColor = AppColors.lightblue; 54 | break; 55 | } 56 | 57 | return AnimatedContainer( 58 | padding: EdgeInsets.only( 59 | top: (pressed) ? BaseStyles.listFieldVertical + BaseStyles.animationOffset : BaseStyles.listFieldVertical, 60 | bottom: (pressed) ? BaseStyles.listFieldVertical - BaseStyles.animationOffset : BaseStyles.listFieldVertical, 61 | left: BaseStyles.listFieldHorizontal, 62 | right: BaseStyles.listFieldHorizontal 63 | ), 64 | child: GestureDetector( 65 | child: Container( 66 | height: ButtonStyles.buttonHeight, 67 | width: MediaQuery.of(context).size.width, 68 | decoration: BoxDecoration( 69 | color: buttonColor, 70 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius), 71 | boxShadow: pressed ? BaseStyles.boxShadowPressed : BaseStyles.boxShadow 72 | ), 73 | child: Center(child: Text(widget.buttonText,style: fontStyle,)), 74 | ), 75 | onTapDown: (details){ 76 | setState(() { 77 | if (widget.buttonType != ButtonType.Disabled) pressed = !pressed; 78 | }); 79 | }, 80 | onTapUp: (details){ 81 | setState(() { 82 | if (widget.buttonType != ButtonType.Disabled) pressed = !pressed; 83 | }); 84 | }, 85 | onTap: (){ 86 | if (widget.buttonType != ButtonType.Disabled) { 87 | widget.onPressed(); 88 | } 89 | }, 90 | ), 91 | duration: Duration(milliseconds: 20), 92 | ); 93 | } 94 | } 95 | 96 | enum ButtonType { LightBlue,Straw, Disabled, DarkGray, DarkBlue } -------------------------------------------------------------------------------- /lib/src/screens/customer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 4 | import 'package:farmers_market/src/styles/tabbar.dart'; 5 | import 'package:farmers_market/src/widgets/customer_scaffold.dart'; 6 | import 'package:farmers_market/src/widgets/navbar.dart'; 7 | import 'package:farmers_market/src/widgets/orders.dart'; 8 | import 'package:farmers_market/src/widgets/products_customer.dart'; 9 | import 'package:farmers_market/src/widgets/products.dart'; 10 | import 'package:farmers_market/src/widgets/profile.dart'; 11 | import 'package:farmers_market/src/widgets/profile_customer.dart'; 12 | import 'package:farmers_market/src/widgets/shopping_bag.dart'; 13 | import 'package:farmers_market/src/widgets/vendor_scaffold.dart'; 14 | import 'package:flutter/cupertino.dart'; 15 | import 'package:flutter/material.dart'; 16 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 17 | import 'dart:io'; 18 | 19 | import 'package:provider/provider.dart'; 20 | 21 | class Customer extends StatefulWidget { 22 | 23 | final String marketId; 24 | 25 | Customer({@required this.marketId}); 26 | 27 | 28 | @override 29 | _CustomerState createState() => _CustomerState(); 30 | 31 | static TabBar get customerTabBar { 32 | return TabBar( 33 | unselectedLabelColor: TabBarStyles.unselectedLabelColor , 34 | labelColor: TabBarStyles.labelColor , 35 | indicatorColor: TabBarStyles.indicatorColor , 36 | tabs: [ 37 | Tab(icon: Icon(Icons.list)), 38 | Tab(icon: Icon(FontAwesomeIcons.shoppingBag)), 39 | Tab(icon: Icon(Icons.person)), 40 | ], 41 | ); 42 | } 43 | } 44 | 45 | class _CustomerState extends State { 46 | StreamSubscription _userSubscription; 47 | 48 | @override 49 | void initState() { 50 | Future.delayed(Duration.zero, (){ 51 | var authBloc = Provider.of(context,listen: false); 52 | _userSubscription = authBloc.user.listen((user) { 53 | if (user == null) Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => false); 54 | }); 55 | }); 56 | 57 | super.initState(); 58 | } 59 | 60 | @override 61 | void dispose() { 62 | _userSubscription.cancel(); 63 | super.dispose(); 64 | } 65 | 66 | @override 67 | Widget build(BuildContext context) { 68 | 69 | if (Platform.isIOS) { 70 | return CupertinoPageScaffold( 71 | child: NestedScrollView( 72 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){ 73 | return [ 74 | AppNavbar.cupertinoNavBar(title: 'Customer Name',context: context), 75 | ]; 76 | }, 77 | body: CustomerScaffold.cupertinoTabScaffold, 78 | ), 79 | ); 80 | } else { 81 | return DefaultTabController( 82 | length: 3, 83 | child: Scaffold( 84 | body: NestedScrollView( 85 | headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){ 86 | return [ 87 | AppNavbar.materialNavBar(title: 'Customer Name', tabBar: Customer.customerTabBar) 88 | ]; 89 | }, 90 | body: TabBarView(children: [ 91 | ProductsCustomer(), 92 | ShoppingBag(), 93 | ProfileCustomer(), 94 | ],) 95 | ) 96 | ), 97 | ); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/src/screens/landing.dart: -------------------------------------------------------------------------------- 1 | import 'package:date_format/date_format.dart'; 2 | import 'package:farmers_market/src/blocs/customer_bloc.dart'; 3 | import 'package:farmers_market/src/models/market.dart'; 4 | import 'package:farmers_market/src/styles/base.dart'; 5 | import 'package:farmers_market/src/styles/colors.dart'; 6 | import 'package:farmers_market/src/styles/text.dart'; 7 | import 'package:farmers_market/src/widgets/list_tile.dart'; 8 | import 'package:farmers_market/src/widgets/sliver_scaffold.dart'; 9 | import 'package:flutter/cupertino.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'dart:io'; 12 | 13 | import 'package:provider/provider.dart'; 14 | 15 | class Landing extends StatelessWidget { 16 | @override 17 | Widget build(BuildContext context) { 18 | var customerBloc = Provider.of(context); 19 | if (Platform.isIOS) { 20 | return AppSliverScaffold.cupertinoSliverScaffold( 21 | navTitle: 'Upcoming Markets', 22 | pageBody: Scaffold(body: pageBody(context,customerBloc)), 23 | ); 24 | } else { 25 | return AppSliverScaffold.materialSliverScaffold( 26 | navTitle: 'Upcoming Markets', pageBody: pageBody(context,customerBloc)); 27 | } 28 | } 29 | 30 | Widget pageBody(BuildContext context, CustomerBloc customerBloc) { 31 | return Column( 32 | mainAxisAlignment: MainAxisAlignment.center, 33 | children: [ 34 | Flexible( 35 | child: Stack( 36 | children: [ 37 | Positioned( 38 | child: Image.asset('assets/images/beans.jpg'), 39 | top: -10.0,), 40 | Positioned( 41 | bottom: 10.0, 42 | right: 10.0, 43 | child: GestureDetector( 44 | child: Container( 45 | decoration: BoxDecoration( 46 | color: AppColors.lightblue, 47 | borderRadius: 48 | BorderRadius.circular(BaseStyles.borderRadius)), 49 | child: Padding( 50 | padding: const EdgeInsets.all(8.0), 51 | child: Text('Vendor Page', 52 | style: TextStyles.buttonTextLight), 53 | ), 54 | ), 55 | onTap: () => Navigator.of(context).pushNamed('/vendor'), 56 | ), 57 | ) 58 | ], 59 | ), 60 | flex: 2, 61 | ), 62 | Flexible( 63 | child: StreamBuilder>( 64 | stream: customerBloc.fetchUpcomingMarkets, 65 | builder: (context, snapshot) { 66 | if (!snapshot.hasData) return Center(child: (Platform.isIOS) 67 | ? CupertinoActivityIndicator() 68 | : CircularProgressIndicator(),); 69 | 70 | return ListView.builder( 71 | itemCount: snapshot.data.length, 72 | itemBuilder: (BuildContext context, int index){ 73 | var market = snapshot.data[index]; 74 | var dateEnd = DateTime.parse(market.dateEnd); 75 | return AppListTile( 76 | marketId: market.marketId, 77 | month: formatDate(dateEnd, ['M']), 78 | date: formatDate(dateEnd, ['d']), 79 | title: market.title, 80 | location: '${market.location.name}, ${market.location.address}, ${market.location.city}, ${market.location.state}', 81 | acceptingOrders: market.acceptingOrders, 82 | ); 83 | }, 84 | ); 85 | } 86 | ), 87 | flex: 3, 88 | ) 89 | ], 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/blocs/auth_bloc.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | 4 | import 'package:farmers_market/src/models/application_user.dart'; 5 | import 'package:farmers_market/src/services/firestore_service.dart'; 6 | import 'package:firebase_auth/firebase_auth.dart'; 7 | import 'package:flutter/services.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | import 'package:rxdart/subjects.dart'; 10 | 11 | final RegExp regExpEmail = RegExp( 12 | r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); 13 | 14 | class AuthBloc { 15 | final _email = BehaviorSubject(); 16 | final _password = BehaviorSubject(); 17 | final _user = BehaviorSubject(); 18 | final _errorMessage = BehaviorSubject(); 19 | final FirebaseAuth _auth = FirebaseAuth.instance; 20 | final FirestoreService _firestoreService = FirestoreService(); 21 | 22 | //Get Data 23 | Stream get email => _email.stream.transform(validateEmail); 24 | Stream get password => _password.stream.transform(validatePassword); 25 | Stream get isValid => CombineLatestStream.combine2(email, password, (email,password)=> true); 26 | Stream get user => _user.stream; 27 | Stream get errorMessage => _errorMessage.stream; 28 | String get userId => _user.value.userId; 29 | 30 | //Set Data 31 | Function(String) get changeEmail => _email.sink.add; 32 | Function(String) get changePassword => _password.sink.add; 33 | 34 | dispose(){ 35 | _email.close(); 36 | _password.close(); 37 | _user.close(); 38 | _errorMessage.close(); 39 | } 40 | 41 | //Transformers 42 | final validateEmail = StreamTransformer.fromHandlers(handleData: (email, sink){ 43 | if (regExpEmail.hasMatch(email.trim())){ 44 | sink.add(email.trim()); 45 | }else { 46 | sink.addError('Must Be Valid Email Address'); 47 | } 48 | }); 49 | 50 | final validatePassword = StreamTransformer.fromHandlers(handleData: (password, sink){ 51 | if (password.length >= 8){ 52 | sink.add(password.trim()); 53 | }else { 54 | sink.addError('8 Character Minimum'); 55 | } 56 | }); 57 | 58 | //Functions 59 | signupEmail() async{ 60 | try{ 61 | UserCredential authResult = await _auth.createUserWithEmailAndPassword(email: _email.value.trim(), password: _password.value.trim()); 62 | var user = ApplicationUser(userId: authResult.user.uid, email: _email.value.trim()); 63 | await _firestoreService.addUser(user); 64 | _user.sink.add(user); 65 | } on PlatformException catch (error){ 66 | print(error); 67 | _errorMessage.sink.add(error.message); 68 | } 69 | } 70 | 71 | loginEmail() async{ 72 | try{ 73 | UserCredential authResult = await _auth.signInWithEmailAndPassword(email: _email.value.trim(), password: _password.value.trim()); 74 | var user = await _firestoreService.fetchUser(authResult.user.uid); 75 | _user.sink.add(user); 76 | } on PlatformException catch (error){ 77 | print(error); 78 | _errorMessage.sink.add(error.message); 79 | } 80 | } 81 | 82 | Future isLoggedIn() async { 83 | var firebaseUser = _auth.currentUser; 84 | if (firebaseUser == null) return false; 85 | 86 | var user = await _firestoreService.fetchUser(firebaseUser.uid); 87 | if (user == null) return false; 88 | 89 | _user.sink.add(user); 90 | return true; 91 | } 92 | 93 | logout() async { 94 | await _auth.signOut(); 95 | _user.sink.add(null); 96 | } 97 | 98 | clearErrorMessage(){ 99 | _errorMessage.sink.add(''); 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /lib/src/widgets/textfield.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/text.dart'; 2 | import 'package:farmers_market/src/styles/textfields.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class AppTextField extends StatefulWidget{ 7 | final bool isIOS; 8 | final String hintText; 9 | final IconData materialIcon; 10 | final IconData cupertinoIcon; 11 | final TextInputType textInputType; 12 | final bool obscureText; 13 | final void Function(String) onChanged; 14 | final String errorText; 15 | final String initialText; 16 | 17 | AppTextField({ 18 | @required this.isIOS, 19 | @required this.hintText, 20 | @required this.cupertinoIcon, 21 | @required this.materialIcon, 22 | this.textInputType = TextInputType.text, 23 | this.obscureText = false, 24 | this.onChanged, 25 | this.errorText, 26 | this.initialText, 27 | }); 28 | 29 | @override 30 | _AppTextFieldState createState() => _AppTextFieldState(); 31 | } 32 | 33 | class _AppTextFieldState extends State { 34 | FocusNode _node; 35 | bool displayCupertinoErrorBorder; 36 | TextEditingController _controller; 37 | 38 | 39 | @override 40 | void initState() { 41 | _node = FocusNode(); 42 | _controller = TextEditingController(); 43 | if (widget.initialText != null) _controller.text = widget.initialText; 44 | _node.addListener(_handleFocusChange); 45 | displayCupertinoErrorBorder = false; 46 | super.initState(); 47 | } 48 | 49 | void _handleFocusChange(){ 50 | if (_node.hasFocus==false && widget.errorText != null){ 51 | displayCupertinoErrorBorder = true; 52 | } else { 53 | displayCupertinoErrorBorder = false; 54 | } 55 | 56 | widget.onChanged(_controller.text); 57 | 58 | } 59 | 60 | @override 61 | void dispose() { 62 | _node.removeListener(_handleFocusChange); 63 | _node.dispose(); 64 | _controller.dispose(); 65 | super.dispose(); 66 | } 67 | 68 | @override 69 | Widget build(BuildContext context) { 70 | if (widget.isIOS){ 71 | return Padding( 72 | padding: EdgeInsets.symmetric(horizontal: TextFieldStyles.textBoxHorizontal, vertical: TextFieldStyles.textBoxVertical), 73 | child: Column( 74 | children: [ 75 | CupertinoTextField( 76 | keyboardType: widget.textInputType, 77 | padding: EdgeInsets.all(12.0), 78 | placeholder: widget.hintText, 79 | placeholderStyle: TextFieldStyles.placeholder, 80 | style: TextFieldStyles.text, 81 | textAlign: TextFieldStyles.textAlign, 82 | cursorColor: TextFieldStyles.cursorColor, 83 | decoration: (displayCupertinoErrorBorder) ? TextFieldStyles.cupertinoErrorDecoration : TextFieldStyles.cupertinoDecoration, 84 | prefix: TextFieldStyles.iconPrefix(widget.cupertinoIcon), 85 | obscureText: widget.obscureText, 86 | onChanged: widget.onChanged, 87 | focusNode: _node, 88 | controller: _controller, 89 | ), 90 | (widget.errorText !=null) ? Padding( 91 | padding: const EdgeInsets.only(top: 5.0,left:10.0), 92 | child: Row(children: [Text(widget.errorText,style: TextStyles.error,)],), 93 | ) : Container() 94 | ], 95 | ), 96 | ); 97 | } else { 98 | return Padding( 99 | padding: EdgeInsets.symmetric(horizontal: TextFieldStyles.textBoxHorizontal, vertical: TextFieldStyles.textBoxVertical), 100 | child: TextField( 101 | keyboardType: widget.textInputType, 102 | cursorColor: TextFieldStyles.cursorColor, 103 | style:TextFieldStyles.text, 104 | textAlign: TextFieldStyles.textAlign, 105 | decoration: TextFieldStyles.materialDecoration(widget.hintText, widget.materialIcon,widget.errorText), 106 | obscureText: widget.obscureText, 107 | controller: _controller, 108 | onChanged: widget.onChanged, 109 | ), 110 | ); 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /lib/src/widgets/products.dart: -------------------------------------------------------------------------------- 1 | import 'package:cupertino_toolbar/cupertino_toolbar.dart'; 2 | import 'package:farmers_market/src/app.dart'; 3 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 4 | import 'package:farmers_market/src/blocs/product_bloc.dart'; 5 | import 'package:farmers_market/src/models/product.dart'; 6 | import 'package:farmers_market/src/styles/colors.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'dart:io'; 10 | 11 | import 'package:provider/provider.dart'; 12 | 13 | import 'card.dart'; 14 | 15 | class Products extends StatelessWidget { 16 | @override 17 | Widget build(BuildContext context) { 18 | var productBloc = Provider.of(context); 19 | var authBloc = Provider.of(context); 20 | 21 | return pageBody(productBloc, context, authBloc.userId); 22 | 23 | 24 | Widget pageBody( 25 | ProductBloc productBloc, BuildContext context, String vendorId) { 26 | return StreamBuilder>( 27 | stream: productBloc.productByVendorId(vendorId), 28 | builder: (context, snapshot) { 29 | if (!snapshot.hasData) 30 | return (Platform.isIOS) 31 | ? CupertinoActivityIndicator() 32 | : CircularProgressIndicator(); 33 | 34 | return Column( 35 | children: [ 36 | Expanded( 37 | child: ListView.builder( 38 | itemCount: snapshot.data.length, 39 | itemBuilder: (context, index) { 40 | var product = snapshot.data[index]; 41 | return GestureDetector( 42 | child: AppCard( 43 | availableUnits: product.availableUnits, 44 | price: product.unitPrice, 45 | productName: product.productName, 46 | unitType: product.unitType, 47 | imageUrl: product.imageUrl, 48 | ), 49 | onTap: () => Navigator.of(context) 50 | .pushNamed('/editproduct/${product.productId}'), 51 | ); 52 | }), 53 | ), 54 | GestureDetector( 55 | child: Container( 56 | height: 50.0, 57 | width: double.infinity, 58 | color: AppColors.straw, 59 | child: (Platform.isIOS) 60 | ? Icon(CupertinoIcons.add, 61 | color: Colors.white, size: 35.0) 62 | : Icon(Icons.add, color: Colors.white, size: 35.0), 63 | ), 64 | onTap: () => Navigator.of(context).pushNamed('/editproduct'), 65 | ) 66 | ], 67 | ); 68 | }); 69 | if (Platform.isIOS) { 70 | return CupertinoPageScaffold( 71 | child: CupertinoToolbar( 72 | items: [ 73 | CupertinoToolbarItem( 74 | icon: CupertinoIcons.add_circled, 75 | onPressed: () => 76 | Navigator.of(context).pushNamed('/editproduct')) 77 | ], 78 | body: pageBody(productBloc,context,authBloc.userId), 79 | ), 80 | ); 81 | } else { 82 | return Scaffold( 83 | body: pageBody(productBloc,context,authBloc.userId), 84 | floatingActionButton: FloatingActionButton( 85 | backgroundColor: AppColors.straw, 86 | child: Icon(Icons.add), 87 | onPressed: () => Navigator.of(context).pushNamed('/editproduct'), 88 | ), 89 | ); 90 | } 91 | } 92 | 93 | Widget pageBody(ProductBloc productBloc, BuildContext context, String vendorId) { 94 | return StreamBuilder>( 95 | stream: productBloc.productByVendorId(vendorId), 96 | builder: (context, snapshot) { 97 | if (!snapshot.hasData) return (Platform.isIOS) 98 | ? CupertinoActivityIndicator() 99 | : CircularProgressIndicator(); 100 | 101 | return ListView.builder( 102 | itemCount: snapshot.data.length, 103 | itemBuilder: (context, index){ 104 | var product = snapshot.data[index]; 105 | return AppCard( 106 | availableUnits: product.availableUnits, 107 | price: product.unitPrice, 108 | productName: product.productName, 109 | unitType: product.unitType, 110 | ); 111 | }); 112 | } 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/widgets/dropdown_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/styles/base.dart'; 2 | import 'package:farmers_market/src/styles/buttons.dart'; 3 | import 'package:farmers_market/src/styles/colors.dart'; 4 | import 'package:farmers_market/src/styles/text.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'dart:io'; 8 | 9 | class AppDropdownButton extends StatelessWidget { 10 | final List items; 11 | final String hintText; 12 | final IconData materialIcon; 13 | final IconData cupertinoIcon; 14 | final String value; 15 | final Function(String) onChanged; 16 | 17 | AppDropdownButton( 18 | {@required this.items, 19 | @required this.hintText, 20 | this.materialIcon, 21 | this.cupertinoIcon, 22 | this.value, 23 | this.onChanged}); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | if (Platform.isIOS) { 28 | return Padding( 29 | padding: BaseStyles.listPadding, 30 | child: Container( 31 | height: ButtonStyles.buttonHeight, 32 | width: MediaQuery.of(context).size.width, 33 | decoration: BoxDecoration( 34 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius), 35 | border: Border.all( 36 | color: AppColors.straw, width: BaseStyles.borderWidth)), 37 | child: Row( 38 | children: [ 39 | Container( 40 | width: 35.0, child: BaseStyles.iconPrefix(materialIcon)), 41 | Expanded( 42 | child: Center( 43 | child: GestureDetector( 44 | child:(value == null) ? Text(hintText, style: TextStyles.suggestion) 45 | : Text(value, style: TextStyles.body), 46 | onTap: () { 47 | showCupertinoModalPopup( 48 | context: context, builder: (BuildContext context) { 49 | return _selectIOS(context, items, value); 50 | }); 51 | }, 52 | )), 53 | ), 54 | ], 55 | ), 56 | ), 57 | ); 58 | } else { 59 | return Padding( 60 | padding: BaseStyles.listPadding, 61 | child: Container( 62 | height: ButtonStyles.buttonHeight, 63 | width: MediaQuery.of(context).size.width, 64 | decoration: BoxDecoration( 65 | borderRadius: BorderRadius.circular(BaseStyles.borderRadius), 66 | border: Border.all( 67 | color: AppColors.straw, width: BaseStyles.borderWidth)), 68 | child: Row( 69 | children: [ 70 | Container( 71 | width: 35.0, child: BaseStyles.iconPrefix(materialIcon)), 72 | Expanded( 73 | child: Center( 74 | child: DropdownButton( 75 | items: buildMaterialItems(items), 76 | value: value, 77 | hint: Text(hintText, style: TextStyles.suggestion), 78 | style: TextStyles.body, 79 | underline: Container(), 80 | iconEnabledColor: AppColors.straw, 81 | onChanged: (value) => onChanged(value), 82 | ), 83 | ), 84 | ), 85 | ], 86 | ), 87 | ), 88 | ); 89 | } 90 | } 91 | 92 | List> buildMaterialItems(List items) { 93 | return (items != null) ? items 94 | .map((item) => DropdownMenuItem( 95 | child: Text( 96 | item, 97 | textAlign: TextAlign.center, 98 | ), 99 | value: item, 100 | )) 101 | .toList() : []; 102 | } 103 | 104 | List buildCupertinoItems(List items) { 105 | return items 106 | .map((item) => Text( 107 | item, 108 | textAlign: TextAlign.center, 109 | style: TextStyles.picker, 110 | )) 111 | .toList(); 112 | } 113 | 114 | _selectIOS(BuildContext context, List items, String value) { 115 | return GestureDetector( 116 | onTap: () { 117 | Navigator.of(context).pop(); 118 | }, 119 | child: Container( 120 | color: Colors.white, 121 | height: 200.0, 122 | child: CupertinoPicker( 123 | scrollController: FixedExtentScrollController(initialItem: items.indexWhere((item) => item == value)), 124 | itemExtent: 45.0, 125 | children: buildCupertinoItems(items), 126 | diameterRatio: 1.0, 127 | onSelectedItemChanged: (int index) => onChanged(items[index]), 128 | ), 129 | ), 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/src/screens/signup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 4 | import 'package:farmers_market/src/styles/base.dart'; 5 | import 'package:farmers_market/src/styles/text.dart'; 6 | import 'package:farmers_market/src/widgets/alerts.dart'; 7 | import 'package:farmers_market/src/widgets/button.dart'; 8 | import 'package:farmers_market/src/widgets/social_button.dart'; 9 | import 'package:farmers_market/src/widgets/textfield.dart'; 10 | import 'package:flutter/cupertino.dart'; 11 | import 'package:flutter/gestures.dart'; 12 | import 'package:flutter/material.dart'; 13 | import 'dart:io'; 14 | 15 | import 'package:provider/provider.dart'; 16 | 17 | class Signup extends StatefulWidget{ 18 | 19 | 20 | @override 21 | _SignupState createState() => _SignupState(); 22 | } 23 | 24 | class _SignupState extends State { 25 | StreamSubscription _userSubscription; 26 | StreamSubscription _errorMessageSubscription; 27 | 28 | @override 29 | void initState() { 30 | final authBloc = Provider.of(context,listen: false); 31 | _userSubscription = authBloc.user.listen((user) { 32 | if (user != null) Navigator.pushReplacementNamed(context, '/landing'); 33 | }); 34 | 35 | _errorMessageSubscription = authBloc.errorMessage.listen((errorMessage) { 36 | if (errorMessage != '' ) { 37 | AppAlerts.showErrorDialog(Platform.isIOS, context, errorMessage).then((_) => authBloc.clearErrorMessage()); 38 | } 39 | }); 40 | super.initState(); 41 | } 42 | 43 | @override 44 | void dispose() { 45 | _userSubscription.cancel(); 46 | _errorMessageSubscription.cancel(); 47 | super.dispose(); 48 | } 49 | 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | final authBloc = Provider.of(context); 54 | if (Platform.isIOS){ 55 | return CupertinoPageScaffold( 56 | child: pageBody(context,authBloc), 57 | ); 58 | } else { 59 | return Scaffold( 60 | body: pageBody(context,authBloc), 61 | ); 62 | } 63 | } 64 | 65 | Widget pageBody(BuildContext context,AuthBloc authBloc) { 66 | return ListView( 67 | padding: EdgeInsets.all(0.0), 68 | children: [ 69 | Container( 70 | height: MediaQuery.of(context).size.height * .2, 71 | decoration: BoxDecoration( 72 | image: DecorationImage( 73 | image: AssetImage('assets/images/top_bg.png'), 74 | fit: BoxFit.fill)), 75 | ), 76 | Container( 77 | height: 200.0, 78 | decoration: BoxDecoration( 79 | image: DecorationImage(image: AssetImage('assets/images/logo.png')), 80 | ), 81 | ), 82 | StreamBuilder( 83 | stream: authBloc.email, 84 | builder: (context, snapshot) { 85 | return AppTextField( 86 | isIOS: Platform.isIOS, 87 | hintText: 'Email', 88 | cupertinoIcon: CupertinoIcons.mail_solid, 89 | materialIcon: Icons.email, 90 | textInputType: TextInputType.emailAddress, 91 | errorText: snapshot.error, 92 | onChanged: authBloc.changeEmail, 93 | ); 94 | } 95 | ), 96 | StreamBuilder( 97 | stream: authBloc.password, 98 | builder: (context, snapshot) { 99 | return AppTextField( 100 | isIOS: Platform.isIOS, 101 | hintText: 'Password', 102 | cupertinoIcon: IconData(0xf4c9,fontFamily: CupertinoIcons.iconFont, fontPackage: CupertinoIcons.iconFontPackage), 103 | materialIcon: Icons.lock, 104 | obscureText: true, 105 | errorText: snapshot.error, 106 | onChanged: authBloc.changePassword, 107 | ); 108 | } 109 | ), 110 | StreamBuilder( 111 | stream: authBloc.isValid, 112 | builder: (context, snapshot) { 113 | return AppButton(buttonText: 'Signup',buttonType: (snapshot.data == true) ? ButtonType.LightBlue : ButtonType.Disabled, onPressed: authBloc.signupEmail,); 114 | } 115 | ), 116 | SizedBox(height: 6.0,), 117 | Center(child: Text('Or',style: TextStyles.suggestion),), 118 | SizedBox(height: 6.0,), 119 | Padding( 120 | padding: BaseStyles.listPadding, 121 | child: Row( 122 | mainAxisAlignment: MainAxisAlignment.center, 123 | children: [ 124 | AppSocialButton(socialType: SocialType.Facebook,), 125 | SizedBox(width:15.0), 126 | AppSocialButton(socialType: SocialType.Google), 127 | ],), 128 | ), 129 | Padding( 130 | padding: BaseStyles.listPadding, 131 | child: RichText( 132 | textAlign: TextAlign.center, 133 | text: TextSpan( 134 | text: 'Already Have an Account? ', 135 | style: TextStyles.body, 136 | children: [ 137 | TextSpan( 138 | text: 'Login', 139 | style: TextStyles.link, 140 | recognizer: TapGestureRecognizer() 141 | ..onTap = () => Navigator.pushNamed(context, '/login') 142 | ) 143 | ] 144 | ) 145 | ), 146 | ) 147 | ], 148 | ); 149 | } 150 | } -------------------------------------------------------------------------------- /lib/src/blocs/vendor_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:farmers_market/src/models/vendor.dart'; 5 | import 'package:farmers_market/src/services/firebase_storage_service.dart'; 6 | import 'package:farmers_market/src/services/firestore_service.dart'; 7 | import 'package:flutter_native_image/flutter_native_image.dart'; 8 | import 'package:image_picker/image_picker.dart'; 9 | import 'package:permission_handler/permission_handler.dart'; 10 | import 'package:rxdart/rxdart.dart'; 11 | import 'package:rxdart/subjects.dart'; 12 | import 'package:uuid/uuid.dart'; 13 | 14 | class VendorBloc { 15 | final _db = FirestoreService(); 16 | final _name = BehaviorSubject(); 17 | final _description = BehaviorSubject(); 18 | final _imageUrl = BehaviorSubject(); 19 | final _vendorId = BehaviorSubject(); 20 | final _vendorSaved = PublishSubject(); 21 | final _vendor = BehaviorSubject(); 22 | final _isUploading = BehaviorSubject(); 23 | 24 | final _picker = ImagePicker(); 25 | final storageService = FirebaseStorageService(); 26 | var uuid = Uuid(); 27 | 28 | //Getters 29 | Future fetchVendor(String userId) => _db.fetchVendor(userId); 30 | Stream get name => _name.stream.transform(validateName); 31 | Stream get description => 32 | _description.stream.transform(validateDescription); 33 | Stream get imageUrl => _imageUrl.stream; 34 | Stream get vendorSaved => _vendorSaved.stream; 35 | Stream get vendor => _vendor.stream; 36 | Stream get isUploading => _isUploading.stream; 37 | Stream get isValid => CombineLatestStream.combine2( 38 | name, description, (a, b) => true); 39 | 40 | //Setters 41 | Function(String) get changeName => _name.sink.add; 42 | Function(String) get changeDescription => _description.sink.add; 43 | Function(String) get changeImageUrl => _imageUrl.sink.add; 44 | Function(Vendor) get changeVendor => _vendor.sink.add; 45 | Function(String) get changeVendorId => _vendorId.sink.add; 46 | 47 | //Dispose 48 | dispose() { 49 | _name.close(); 50 | _description.close(); 51 | _imageUrl.close(); 52 | _vendorSaved.close(); 53 | _vendor.close(); 54 | _vendorId.close(); 55 | _isUploading.close(); 56 | } 57 | 58 | //Validators 59 | final validateName = 60 | StreamTransformer.fromHandlers(handleData: (name, sink) { 61 | if (name != null) { 62 | if (name.length >= 3 && name.length <= 20) { 63 | sink.add(name.trim()); 64 | } else { 65 | if (name.length < 3) { 66 | sink.addError('3 Character Minimum'); 67 | } else { 68 | sink.addError('20 Character Maximum'); 69 | } 70 | } 71 | } 72 | }); 73 | 74 | final validateDescription = StreamTransformer.fromHandlers( 75 | handleData: (description, sink) { 76 | if (description != null) { 77 | if (description.length >= 10 && description.length <= 200) { 78 | sink.add(description.trim()); 79 | } else { 80 | if (description.length < 10) { 81 | sink.addError('10 Character Minimum'); 82 | } else { 83 | sink.addError('200 Character Maximum'); 84 | } 85 | } 86 | } 87 | }); 88 | 89 | //Save Record 90 | Future saveVendor() async { 91 | var vendor = Vendor( 92 | description: _description.value, 93 | imageUrl: _imageUrl.value, 94 | name: _name.value, 95 | vendorId: _vendorId.value); 96 | 97 | return _db.setVendor(vendor).then((value) { 98 | _vendorSaved.sink.add(true); 99 | changeVendor(vendor); 100 | }).catchError((error) => _vendorSaved.sink.add(false)); 101 | } 102 | 103 | pickImage() async { 104 | PickedFile image; 105 | File croppedFile; 106 | 107 | await Permission.photos.request(); 108 | 109 | var permissionStatus = await Permission.photos.status; 110 | if (permissionStatus.isGranted) { 111 | //Get Image From Device 112 | image = await _picker.getImage(source: ImageSource.gallery); 113 | 114 | //Upload to Firebase 115 | if (image != null) { 116 | _isUploading.sink.add(true); 117 | 118 | //Get Image Properties 119 | ImageProperties properties = 120 | await FlutterNativeImage.getImageProperties(image.path); 121 | 122 | //CropImage 123 | if (properties.height > properties.width) { 124 | var yoffset = (properties.height - properties.width) / 2; 125 | croppedFile = await FlutterNativeImage.cropImage(image.path, 0, 126 | yoffset.toInt(), properties.width, properties.width); 127 | } else if (properties.width > properties.height) { 128 | var xoffset = (properties.width - properties.height) / 2; 129 | croppedFile = await FlutterNativeImage.cropImage(image.path, 130 | xoffset.toInt(), 0, properties.height, properties.height); 131 | } else { 132 | croppedFile = File(image.path); 133 | } 134 | 135 | //Resize 136 | File compressedFile = await FlutterNativeImage.compressImage( 137 | croppedFile.path, 138 | quality: 100, 139 | targetHeight: 600, 140 | targetWidth: 600); 141 | 142 | var imageUrl = 143 | await storageService.uploadVendorImage(compressedFile, uuid.v4()); 144 | changeImageUrl(imageUrl); 145 | _isUploading.sink.add(false); 146 | } else { 147 | print('No Path Received'); 148 | } 149 | } else { 150 | print('Grant Permissions and try again'); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/src/screens/login.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 4 | import 'package:farmers_market/src/styles/base.dart'; 5 | import 'package:farmers_market/src/styles/text.dart'; 6 | import 'package:farmers_market/src/widgets/alerts.dart'; 7 | import 'package:farmers_market/src/widgets/button.dart'; 8 | import 'package:farmers_market/src/widgets/social_button.dart'; 9 | import 'package:farmers_market/src/widgets/textfield.dart'; 10 | import 'package:flutter/cupertino.dart'; 11 | import 'package:flutter/gestures.dart'; 12 | import 'package:flutter/material.dart'; 13 | import 'dart:io'; 14 | 15 | import 'package:provider/provider.dart'; 16 | 17 | class Login extends StatefulWidget { 18 | 19 | 20 | @override 21 | _LoginState createState() => _LoginState(); 22 | } 23 | 24 | class _LoginState extends State { 25 | StreamSubscription _userSubscription; 26 | StreamSubscription _errorMessageSubscription; 27 | 28 | @override 29 | void initState() { 30 | final authBloc = Provider.of(context,listen: false); 31 | _userSubscription = authBloc.user.listen((user) { 32 | if (user != null) Navigator.pushReplacementNamed(context, '/landing'); 33 | }); 34 | 35 | _errorMessageSubscription = authBloc.errorMessage.listen((errorMessage) { 36 | if (errorMessage != '' ) { 37 | AppAlerts.showErrorDialog(Platform.isIOS, context, errorMessage).then((_) => authBloc.clearErrorMessage()); 38 | } 39 | }); 40 | super.initState(); 41 | } 42 | 43 | @override 44 | void dispose() { 45 | _userSubscription.cancel(); 46 | _errorMessageSubscription.cancel(); 47 | super.dispose(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | final authBloc = Provider.of(context); 53 | 54 | if (Platform.isIOS) { 55 | return CupertinoPageScaffold( 56 | child: pageBody(context, authBloc), 57 | ); 58 | } else { 59 | return Scaffold( 60 | body: pageBody(context, authBloc), 61 | ); 62 | } 63 | } 64 | 65 | Widget pageBody(BuildContext context, AuthBloc authBloc) { 66 | return ListView( 67 | padding: EdgeInsets.all(0.0), 68 | children: [ 69 | Container( 70 | height: MediaQuery.of(context).size.height * .2, 71 | decoration: BoxDecoration( 72 | image: DecorationImage( 73 | image: AssetImage('assets/images/top_bg.png'), 74 | fit: BoxFit.fill)), 75 | ), 76 | Container( 77 | height: 200.0, 78 | decoration: BoxDecoration( 79 | image: DecorationImage(image: AssetImage('assets/images/logo.png')), 80 | ), 81 | ), 82 | StreamBuilder( 83 | stream: authBloc.email, 84 | builder: (context, snapshot) { 85 | return AppTextField( 86 | isIOS: Platform.isIOS, 87 | hintText: 'Email', 88 | cupertinoIcon: CupertinoIcons.mail_solid, 89 | materialIcon: Icons.email, 90 | textInputType: TextInputType.emailAddress, 91 | errorText: snapshot.error, 92 | onChanged: authBloc.changeEmail, 93 | ); 94 | }), 95 | StreamBuilder( 96 | stream: authBloc.password, 97 | builder: (context, snapshot) { 98 | return AppTextField( 99 | isIOS: Platform.isIOS, 100 | hintText: 'Password', 101 | cupertinoIcon: IconData(0xf4c9, 102 | fontFamily: CupertinoIcons.iconFont, 103 | fontPackage: CupertinoIcons.iconFontPackage), 104 | materialIcon: Icons.lock, 105 | obscureText: true, 106 | errorText: snapshot.error, 107 | onChanged: authBloc.changePassword, 108 | ); 109 | }), 110 | StreamBuilder( 111 | stream: authBloc.isValid, 112 | builder: (context, snapshot) { 113 | return AppButton( 114 | buttonText: 'Login', 115 | buttonType: (snapshot.data == true) 116 | ? ButtonType.LightBlue 117 | : ButtonType.Disabled, 118 | onPressed: authBloc.loginEmail, 119 | ); 120 | }), 121 | SizedBox( 122 | height: 6.0, 123 | ), 124 | Center( 125 | child: Text('Or', style: TextStyles.suggestion), 126 | ), 127 | SizedBox( 128 | height: 6.0, 129 | ), 130 | Padding( 131 | padding: BaseStyles.listPadding, 132 | child: Row( 133 | mainAxisAlignment: MainAxisAlignment.center, 134 | children: [ 135 | AppSocialButton( 136 | socialType: SocialType.Facebook, 137 | ), 138 | SizedBox(width: 15.0), 139 | AppSocialButton(socialType: SocialType.Google), 140 | ], 141 | ), 142 | ), 143 | Padding( 144 | padding: BaseStyles.listPadding, 145 | child: RichText( 146 | textAlign: TextAlign.center, 147 | text: TextSpan( 148 | text: 'New Here? ', 149 | style: TextStyles.body, 150 | children: [ 151 | TextSpan( 152 | text: 'Signup', 153 | style: TextStyles.link, 154 | recognizer: TapGestureRecognizer() 155 | ..onTap = 156 | () => Navigator.pushNamed(context, '/signup')) 157 | ])), 158 | ) 159 | ], 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /lib/src/blocs/product_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:farmers_market/src/models/product.dart'; 5 | import 'package:farmers_market/src/services/firebase_storage_service.dart'; 6 | import 'package:farmers_market/src/services/firestore_service.dart'; 7 | import 'package:flutter_native_image/flutter_native_image.dart'; 8 | import 'package:image_picker/image_picker.dart'; 9 | import 'package:permission_handler/permission_handler.dart'; 10 | import 'package:rxdart/rxdart.dart'; 11 | import 'package:rxdart/subjects.dart'; 12 | import 'package:uuid/uuid.dart'; 13 | 14 | class ProductBloc { 15 | final _productName = BehaviorSubject(); 16 | final _unitType = BehaviorSubject(); 17 | final _unitPrice = BehaviorSubject(); 18 | final _availableUnits = BehaviorSubject(); 19 | final _imageUrl = BehaviorSubject(); 20 | final _vendorId = BehaviorSubject(); 21 | final _productSaved = PublishSubject(); 22 | final _product = BehaviorSubject(); 23 | final _isUploading = BehaviorSubject(); 24 | 25 | final db = FirestoreService(); 26 | var uuid = Uuid(); 27 | final _picker = ImagePicker(); 28 | final storageService = FirebaseStorageService(); 29 | 30 | //Get 31 | Stream get productName => 32 | _productName.stream.transform(validateProductName); 33 | Stream get unitType => _unitType.stream; 34 | Stream get unitPrice => 35 | _unitPrice.stream.transform(validateUnitPrice); 36 | Stream get availableUnits => 37 | _availableUnits.stream.transform(validateAvailableUnits); 38 | Stream get imageUrl => _imageUrl.stream; 39 | Stream get isValid => CombineLatestStream.combine4( 40 | productName, unitType, unitPrice, availableUnits, (a, b, c, d) => true); 41 | Stream> productByVendorId(String vendorId) => 42 | db.fetchProductsByVendorId(vendorId); 43 | Stream get productSaved => _productSaved.stream; 44 | Future fetchProduct(String productId) => db.fetchProduct(productId); 45 | Stream get isUploading => _isUploading.stream; 46 | 47 | 48 | //Set 49 | Function(String) get changeProductName => _productName.sink.add; 50 | Function(String) get changeUnitType => _unitType.sink.add; 51 | Function(String) get changeUnitPrice => _unitPrice.sink.add; 52 | Function(String) get changeAvailableUnits => _availableUnits.sink.add; 53 | Function(String) get changeImageUrl => _imageUrl.sink.add; 54 | Function(String) get changeVendorId => _vendorId.sink.add; 55 | Function(Product) get changeProduct => _product.sink.add; 56 | 57 | dispose() { 58 | _productName.close(); 59 | _unitType.close(); 60 | _unitPrice.close(); 61 | _availableUnits.close(); 62 | _vendorId.close(); 63 | _productSaved.close(); 64 | _product.close(); 65 | _imageUrl.close(); 66 | _isUploading.close(); 67 | } 68 | 69 | //Functions 70 | Future saveProduct() async { 71 | var product = Product( 72 | approved: (_product.value == null) ? true : _product.value.approved, 73 | availableUnits: int.parse(_availableUnits.value), 74 | productId: 75 | (_product.value == null) ? uuid.v4() : _product.value.productId, 76 | productName: _productName.value.trim(), 77 | unitPrice: double.parse(_unitPrice.value), 78 | unitType: _unitType.value, 79 | vendorId: _vendorId.value, 80 | imageUrl: _imageUrl.value 81 | ); 82 | 83 | return db 84 | .setProduct(product) 85 | .then((value) => _productSaved.sink.add(true)) 86 | .catchError((error) => _productSaved.sink.add(false)); 87 | } 88 | 89 | pickImage() async { 90 | PickedFile image; 91 | File croppedFile; 92 | 93 | await Permission.photos.request(); 94 | 95 | var permissionStatus = await Permission.photos.status; 96 | if (permissionStatus.isGranted) { 97 | //Get Image From Device 98 | image = await _picker.getImage(source: ImageSource.gallery); 99 | 100 | //Upload to Firebase 101 | if (image != null) { 102 | _isUploading.sink.add(true); 103 | 104 | //Get Image Properties 105 | ImageProperties properties = await FlutterNativeImage.getImageProperties(image.path); 106 | 107 | //CropImage 108 | if (properties.height > properties.width){ 109 | var yoffset = (properties.height - properties.width)/2; 110 | croppedFile = await FlutterNativeImage.cropImage(image.path, 0, yoffset.toInt(), properties.width, properties.width); 111 | } else if (properties.width > properties.height) { 112 | var xoffset = (properties.width- properties.height)/2; 113 | croppedFile = await FlutterNativeImage.cropImage(image.path, xoffset.toInt(), 0, properties.height, properties.height); 114 | } else { 115 | croppedFile = File(image.path); 116 | } 117 | 118 | //Resize 119 | File compressedFile = await FlutterNativeImage.compressImage(croppedFile.path,quality: 100, targetHeight: 600, targetWidth: 600); 120 | 121 | var imageUrl = await storageService.uploadProductImage( 122 | compressedFile, uuid.v4()); 123 | changeImageUrl(imageUrl); 124 | _isUploading.sink.add(false); 125 | } else { 126 | print('No Path Received'); 127 | } 128 | } else { 129 | print('Grant Permissions and try again'); 130 | } 131 | } 132 | 133 | //Validators 134 | final validateUnitPrice = StreamTransformer.fromHandlers( 135 | handleData: (unitPrice, sink) { 136 | if (unitPrice != null) { 137 | try { 138 | sink.add(double.parse(unitPrice)); 139 | } catch (error) { 140 | sink.addError('Must be a number'); 141 | } 142 | } 143 | }); 144 | 145 | final validateAvailableUnits = StreamTransformer.fromHandlers( 146 | handleData: (availableUnits, sink) { 147 | if (availableUnits != null) { 148 | try { 149 | sink.add(int.parse(availableUnits)); 150 | } catch (error) { 151 | sink.addError('Must be a whole number'); 152 | } 153 | } 154 | }); 155 | 156 | final validateProductName = StreamTransformer.fromHandlers( 157 | handleData: (productName, sink) { 158 | if (productName != null) { 159 | if (productName.length >= 3 && productName.length <= 20) { 160 | sink.add(productName.trim()); 161 | } else { 162 | if (productName.length < 3) { 163 | sink.addError('3 Character Minimum'); 164 | } else { 165 | sink.addError('20 Character Maximum'); 166 | } 167 | } 168 | } 169 | }); 170 | } 171 | -------------------------------------------------------------------------------- /lib/src/screens/edit_vendor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 5 | import 'package:farmers_market/src/blocs/product_bloc.dart'; 6 | import 'package:farmers_market/src/blocs/vendor_bloc.dart'; 7 | import 'package:farmers_market/src/models/vendor.dart'; 8 | import 'package:farmers_market/src/styles/base.dart'; 9 | import 'package:farmers_market/src/styles/colors.dart'; 10 | import 'package:farmers_market/src/styles/text.dart'; 11 | import 'package:farmers_market/src/widgets/button.dart'; 12 | import 'package:farmers_market/src/widgets/sliver_scaffold.dart'; 13 | import 'package:farmers_market/src/widgets/textfield.dart'; 14 | import 'package:flutter/cupertino.dart'; 15 | import 'package:flutter/material.dart'; 16 | import 'package:fluttertoast/fluttertoast.dart'; 17 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 18 | import 'package:provider/provider.dart'; 19 | 20 | class EditVendor extends StatefulWidget { 21 | final String vendorId; 22 | 23 | EditVendor({this.vendorId}); 24 | 25 | @override 26 | _EditVendorState createState() => _EditVendorState(); 27 | } 28 | 29 | class _EditVendorState extends State { 30 | StreamSubscription _savedSubscription; 31 | @override 32 | void initState() { 33 | var vendorBloc = Provider.of(context, listen: false); 34 | _savedSubscription = vendorBloc.vendorSaved.listen((saved) { 35 | if (saved != null && saved == true && context != null) { 36 | Fluttertoast.showToast( 37 | msg: "Vendor Saved", 38 | toastLength: Toast.LENGTH_SHORT, 39 | gravity: ToastGravity.CENTER, 40 | timeInSecForIosWeb: 2, 41 | backgroundColor: AppColors.lightblue, 42 | textColor: Colors.white, 43 | fontSize: 16.0); 44 | 45 | Navigator.of(context).pop(); 46 | } 47 | }); 48 | super.initState(); 49 | } 50 | 51 | @override 52 | void dispose() { 53 | _savedSubscription.cancel(); 54 | super.dispose(); 55 | } 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | var vendorBloc = Provider.of(context); 60 | var authBloc = Provider.of(context); 61 | 62 | return StreamBuilder( 63 | stream: vendorBloc.vendor, 64 | builder: (context, snapshot) { 65 | if (!snapshot.hasData && widget.vendorId != null) { 66 | return Scaffold( 67 | body: Center( 68 | child: (Platform.isIOS) 69 | ? CupertinoActivityIndicator() 70 | : CircularProgressIndicator()), 71 | ); 72 | } 73 | 74 | Vendor vendor = snapshot.data; 75 | 76 | if (vendor != null) { 77 | //Edit Logic 78 | loadValues(vendorBloc, vendor, authBloc.userId); 79 | } else { 80 | //Add Logic 81 | loadValues(vendorBloc, null, authBloc.userId); 82 | } 83 | 84 | return (Platform.isIOS) 85 | ? AppSliverScaffold.cupertinoSliverScaffold( 86 | navTitle: '', 87 | pageBody: pageBody(true, vendorBloc, context, vendor), 88 | context: context) 89 | : AppSliverScaffold.materialSliverScaffold( 90 | navTitle: '', 91 | pageBody: pageBody(false, vendorBloc, context, vendor), 92 | context: context); 93 | }, 94 | ); 95 | } 96 | 97 | Widget pageBody(bool isIOS, VendorBloc vendorBloc, BuildContext context, 98 | Vendor existingVendor) { 99 | var pageLabel = (existingVendor != null) ? 'Edit Profile' : 'Add Profile'; 100 | return ListView( 101 | children: [ 102 | Text( 103 | pageLabel, 104 | style: TextStyles.subtitle, 105 | textAlign: TextAlign.center, 106 | ), 107 | Padding( 108 | padding: BaseStyles.listPadding, 109 | child: Divider(color: AppColors.darkblue), 110 | ), 111 | StreamBuilder( 112 | stream: vendorBloc.name, 113 | builder: (context, snapshot) { 114 | return AppTextField( 115 | hintText: 'Vendor Name', 116 | cupertinoIcon: FontAwesomeIcons.shoppingBasket, 117 | materialIcon: FontAwesomeIcons.shoppingBasket, 118 | isIOS: isIOS, 119 | errorText: snapshot.error, 120 | initialText: 121 | (existingVendor != null) ? existingVendor.name : null, 122 | onChanged: vendorBloc.changeName, 123 | ); 124 | }), 125 | StreamBuilder( 126 | stream: vendorBloc.description, 127 | builder: (context, snapshot) { 128 | return AppTextField( 129 | hintText: 'Description', 130 | cupertinoIcon: FontAwesomeIcons.shoppingBasket, 131 | materialIcon: FontAwesomeIcons.shoppingBasket, 132 | isIOS: isIOS, 133 | errorText: snapshot.error, 134 | initialText: (existingVendor != null) 135 | ? existingVendor.description 136 | : null, 137 | onChanged: vendorBloc.changeDescription, 138 | ); 139 | }), 140 | StreamBuilder( 141 | stream: vendorBloc.isUploading, 142 | builder: (context, snapshot) { 143 | return (!snapshot.hasData || snapshot.data == false) 144 | ? Container() 145 | : Center( 146 | child: (Platform.isIOS) 147 | ? CupertinoActivityIndicator() 148 | : CircularProgressIndicator(), 149 | ); 150 | }, 151 | ), 152 | StreamBuilder( 153 | stream: vendorBloc.imageUrl, 154 | builder: (context, snapshot) { 155 | if (!snapshot.hasData || snapshot.data == "") 156 | return AppButton( 157 | buttonType: ButtonType.Straw, 158 | buttonText: 'Add Image', 159 | onPressed: vendorBloc.pickImage, 160 | ); 161 | 162 | return Column( 163 | children: [ 164 | Padding( 165 | padding: BaseStyles.listPadding, 166 | child: Image.network(snapshot.data), 167 | ), 168 | AppButton( 169 | buttonType: ButtonType.Straw, 170 | buttonText: 'Change Image', 171 | onPressed: vendorBloc.pickImage, 172 | ) 173 | ], 174 | ); 175 | }), 176 | StreamBuilder( 177 | stream: vendorBloc.isValid, 178 | builder: (context, snapshot) { 179 | return AppButton( 180 | buttonType: (snapshot.data == true) 181 | ? ButtonType.DarkBlue 182 | : ButtonType.Disabled, 183 | buttonText: 'Save Profile', 184 | onPressed: vendorBloc.saveVendor, 185 | ); 186 | }), 187 | ], 188 | ); 189 | } 190 | 191 | loadValues(VendorBloc vendorBloc, Vendor vendor, String vendorId) { 192 | vendorBloc.changeVendorId(vendorId); 193 | 194 | if (vendor != null) { 195 | //Edit 196 | vendorBloc.changeName(vendor.name); 197 | vendorBloc.changeDescription(vendor.description); 198 | vendorBloc.changeImageUrl(vendor.imageUrl ?? ''); 199 | } else { 200 | //Add 201 | vendorBloc.changeName(null); 202 | vendorBloc.changeDescription(null); 203 | vendorBloc.changeImageUrl(''); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /lib/src/screens/edit_product.dart: -------------------------------------------------------------------------------- 1 | import 'package:farmers_market/src/app.dart'; 2 | import 'package:farmers_market/src/blocs/auth_bloc.dart'; 3 | import 'package:farmers_market/src/blocs/product_bloc.dart'; 4 | import 'package:farmers_market/src/models/product.dart'; 5 | import 'package:farmers_market/src/models/application_user.dart'; 6 | import 'package:farmers_market/src/styles/base.dart'; 7 | import 'package:farmers_market/src/styles/colors.dart'; 8 | import 'package:farmers_market/src/styles/text.dart'; 9 | import 'package:farmers_market/src/widgets/button.dart'; 10 | import 'package:farmers_market/src/widgets/dropdown_button.dart'; 11 | import 'package:farmers_market/src/widgets/sliver_scaffold.dart'; 12 | import 'package:farmers_market/src/widgets/textfield.dart'; 13 | import 'package:flutter/cupertino.dart'; 14 | import 'package:flutter/material.dart'; 15 | import 'package:fluttertoast/fluttertoast.dart'; 16 | import 'dart:io'; 17 | 18 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 19 | import 'package:provider/provider.dart'; 20 | 21 | class EditProduct extends StatefulWidget { 22 | final String productId; 23 | 24 | EditProduct({this.productId}); 25 | 26 | @override 27 | _EditProductState createState() => _EditProductState(); 28 | } 29 | 30 | class _EditProductState extends State { 31 | @override 32 | void initState() { 33 | var productBloc = Provider.of(context, listen: false); 34 | productBloc.productSaved.listen((saved) { 35 | if (saved != null && saved == true && context != null) { 36 | Fluttertoast.showToast( 37 | msg: "Product Saved", 38 | toastLength: Toast.LENGTH_SHORT, 39 | gravity: ToastGravity.CENTER, 40 | timeInSecForIosWeb: 2, 41 | backgroundColor: AppColors.lightblue, 42 | textColor: Colors.white, 43 | fontSize: 16.0); 44 | 45 | Navigator.of(context).pop(); 46 | } 47 | }); 48 | super.initState(); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | var productBloc = Provider.of(context); 54 | var authBloc = Provider.of(context); 55 | 56 | return FutureBuilder( 57 | future: productBloc.fetchProduct(widget.productId), 58 | builder: (context, snapshot) { 59 | if (!snapshot.hasData && widget.productId != null) { 60 | return Scaffold( 61 | body: Center( 62 | child: (Platform.isIOS) 63 | ? CupertinoActivityIndicator() 64 | : CircularProgressIndicator()), 65 | ); 66 | } 67 | 68 | Product existingProduct; 69 | 70 | if (widget.productId != null) { 71 | //Edit Logic 72 | existingProduct = snapshot.data; 73 | loadValues(productBloc, existingProduct, authBloc.userId); 74 | } else { 75 | //Add Logic 76 | loadValues(productBloc, null, authBloc.userId); 77 | } 78 | 79 | return (Platform.isIOS) 80 | ? AppSliverScaffold.cupertinoSliverScaffold( 81 | navTitle: '', 82 | pageBody: pageBody(true, productBloc, context, existingProduct), 83 | context: context) 84 | : AppSliverScaffold.materialSliverScaffold( 85 | navTitle: '', 86 | pageBody: 87 | pageBody(false, productBloc, context, existingProduct), 88 | context: context); 89 | }, 90 | ); 91 | } 92 | 93 | Widget pageBody(bool isIOS, ProductBloc productBloc, BuildContext context, 94 | Product existingProduct) { 95 | var items = Provider.of>(context); 96 | var pageLabel = (existingProduct != null) ? 'Edit Product' : 'Add Product'; 97 | return ListView( 98 | children: [ 99 | Text( 100 | pageLabel, 101 | style: TextStyles.subtitle, 102 | textAlign: TextAlign.center, 103 | ), 104 | Padding( 105 | padding: BaseStyles.listPadding, 106 | child: Divider(color: AppColors.darkblue), 107 | ), 108 | StreamBuilder( 109 | stream: productBloc.productName, 110 | builder: (context, snapshot) { 111 | return AppTextField( 112 | hintText: 'Product Name', 113 | cupertinoIcon: FontAwesomeIcons.shoppingBasket, 114 | materialIcon: FontAwesomeIcons.shoppingBasket, 115 | isIOS: isIOS, 116 | errorText: snapshot.error, 117 | initialText: (existingProduct != null) 118 | ? existingProduct.productName 119 | : null, 120 | onChanged: productBloc.changeProductName, 121 | ); 122 | }), 123 | StreamBuilder( 124 | stream: productBloc.unitType, 125 | builder: (context, snapshot) { 126 | return AppDropdownButton( 127 | hintText: 'Unit Type', 128 | items: items, 129 | value: snapshot.data, 130 | materialIcon: FontAwesomeIcons.balanceScale, 131 | cupertinoIcon: FontAwesomeIcons.balanceScale, 132 | onChanged: productBloc.changeUnitType, 133 | ); 134 | }), 135 | StreamBuilder( 136 | stream: productBloc.unitPrice, 137 | builder: (context, snapshot) { 138 | return AppTextField( 139 | hintText: 'Unit Price', 140 | cupertinoIcon: FontAwesomeIcons.tag, 141 | materialIcon: FontAwesomeIcons.tag, 142 | isIOS: isIOS, 143 | textInputType: TextInputType.number, 144 | errorText: snapshot.error, 145 | initialText: (existingProduct != null) 146 | ? existingProduct.unitPrice.toString() 147 | : null, 148 | onChanged: productBloc.changeUnitPrice, 149 | ); 150 | }), 151 | StreamBuilder( 152 | stream: productBloc.availableUnits, 153 | builder: (context, snapshot) { 154 | return AppTextField( 155 | hintText: 'Available Units', 156 | cupertinoIcon: FontAwesomeIcons.cubes, 157 | materialIcon: FontAwesomeIcons.cubes, 158 | isIOS: isIOS, 159 | textInputType: TextInputType.number, 160 | errorText: snapshot.error, 161 | initialText: (existingProduct != null) 162 | ? existingProduct.availableUnits.toString() 163 | : null, 164 | onChanged: productBloc.changeAvailableUnits, 165 | ); 166 | }), 167 | StreamBuilder( 168 | stream: productBloc.isUploading, 169 | builder: (context,snapshot){ 170 | return (!snapshot.hasData || snapshot.data == false) 171 | ? Container() 172 | : Center(child: (Platform.isIOS) 173 | ? CupertinoActivityIndicator() 174 | : CircularProgressIndicator(),); 175 | },), 176 | StreamBuilder( 177 | stream: productBloc.imageUrl, 178 | builder: (context, snapshot) { 179 | if (!snapshot.hasData || snapshot.data == "") 180 | return AppButton(buttonType: ButtonType.Straw, buttonText: 'Add Image',onPressed: productBloc.pickImage,); 181 | 182 | return Column(children: [ 183 | Padding( 184 | padding: BaseStyles.listPadding, 185 | child: Image.network(snapshot.data), 186 | ), 187 | AppButton(buttonType: ButtonType.Straw, buttonText: 'Change Image',onPressed: productBloc.pickImage,) 188 | ],); 189 | } 190 | ), 191 | StreamBuilder( 192 | stream: productBloc.isValid, 193 | builder: (context, snapshot) { 194 | return AppButton( 195 | buttonType: (snapshot.data == true) 196 | ? ButtonType.DarkBlue 197 | : ButtonType.Disabled, 198 | buttonText: 'Save Product', 199 | onPressed: productBloc.saveProduct, 200 | ); 201 | }), 202 | ], 203 | ); 204 | } 205 | 206 | loadValues(ProductBloc productBloc, Product product, String vendorId) { 207 | productBloc.changeProduct(product); 208 | productBloc.changeVendorId(vendorId); 209 | 210 | if (product != null) { 211 | //Edit 212 | productBloc.changeUnitType(product.unitType); 213 | productBloc.changeProductName(product.productName); 214 | productBloc.changeUnitPrice(product.unitPrice.toString()); 215 | productBloc.changeAvailableUnits(product.availableUnits.toString()); 216 | productBloc.changeImageUrl(product.imageUrl ?? ''); 217 | } else { 218 | //Add 219 | productBloc.changeUnitType(null); 220 | productBloc.changeProductName(null); 221 | productBloc.changeUnitPrice(null); 222 | productBloc.changeAvailableUnits(null); 223 | productBloc.changeImageUrl(''); 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.4.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.0.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.0.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.3" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | cloud_firestore: 40 | dependency: "direct main" 41 | description: 42 | name: cloud_firestore 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "0.14.0+2" 46 | cloud_firestore_platform_interface: 47 | dependency: transitive 48 | description: 49 | name: cloud_firestore_platform_interface 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.0.1" 53 | cloud_firestore_web: 54 | dependency: transitive 55 | description: 56 | name: cloud_firestore_web 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.2.0+1" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.14.13" 67 | convert: 68 | dependency: transitive 69 | description: 70 | name: convert 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.1" 74 | crypto: 75 | dependency: transitive 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.4" 81 | cupertino_icons: 82 | dependency: "direct main" 83 | description: 84 | name: cupertino_icons 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.1.3" 88 | date_format: 89 | dependency: "direct main" 90 | description: 91 | name: date_format 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.0.8" 95 | fake_async: 96 | dependency: transitive 97 | description: 98 | name: fake_async 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.1.0" 102 | firebase: 103 | dependency: transitive 104 | description: 105 | name: firebase 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "7.3.0" 109 | firebase_auth: 110 | dependency: "direct main" 111 | description: 112 | name: firebase_auth 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "0.18.0+1" 116 | firebase_auth_platform_interface: 117 | dependency: transitive 118 | description: 119 | name: firebase_auth_platform_interface 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "2.0.1" 123 | firebase_auth_web: 124 | dependency: transitive 125 | description: 126 | name: firebase_auth_web 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "0.3.0+1" 130 | firebase_core: 131 | dependency: "direct main" 132 | description: 133 | name: firebase_core 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "0.5.0" 137 | firebase_core_platform_interface: 138 | dependency: transitive 139 | description: 140 | name: firebase_core_platform_interface 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "2.0.0" 144 | firebase_core_web: 145 | dependency: transitive 146 | description: 147 | name: firebase_core_web 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "0.2.0" 151 | firebase_storage: 152 | dependency: "direct main" 153 | description: 154 | name: firebase_storage 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "4.0.0" 158 | flutter: 159 | dependency: "direct main" 160 | description: flutter 161 | source: sdk 162 | version: "0.0.0" 163 | flutter_native_image: 164 | dependency: "direct main" 165 | description: 166 | name: flutter_native_image 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "0.0.5+2" 170 | flutter_plugin_android_lifecycle: 171 | dependency: transitive 172 | description: 173 | name: flutter_plugin_android_lifecycle 174 | url: "https://pub.dartlang.org" 175 | source: hosted 176 | version: "1.0.8" 177 | flutter_test: 178 | dependency: "direct dev" 179 | description: flutter 180 | source: sdk 181 | version: "0.0.0" 182 | flutter_web_plugins: 183 | dependency: transitive 184 | description: flutter 185 | source: sdk 186 | version: "0.0.0" 187 | fluttertoast: 188 | dependency: "direct main" 189 | description: 190 | name: fluttertoast 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "4.0.1" 194 | font_awesome_flutter: 195 | dependency: "direct main" 196 | description: 197 | name: font_awesome_flutter 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "8.8.1" 201 | google_fonts: 202 | dependency: "direct main" 203 | description: 204 | name: google_fonts 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.0.0" 208 | http: 209 | dependency: transitive 210 | description: 211 | name: http 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "0.12.1" 215 | http_parser: 216 | dependency: transitive 217 | description: 218 | name: http_parser 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "3.1.4" 222 | image_picker: 223 | dependency: "direct main" 224 | description: 225 | name: image_picker 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "0.6.7+3" 229 | image_picker_platform_interface: 230 | dependency: transitive 231 | description: 232 | name: image_picker_platform_interface 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "1.1.0" 236 | intl: 237 | dependency: "direct main" 238 | description: 239 | name: intl 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "0.16.1" 243 | js: 244 | dependency: transitive 245 | description: 246 | name: js 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "0.6.1+1" 250 | matcher: 251 | dependency: transitive 252 | description: 253 | name: matcher 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "0.12.8" 257 | meta: 258 | dependency: transitive 259 | description: 260 | name: meta 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "1.1.8" 264 | nested: 265 | dependency: transitive 266 | description: 267 | name: nested 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "0.0.4" 271 | path: 272 | dependency: transitive 273 | description: 274 | name: path 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "1.7.0" 278 | path_provider: 279 | dependency: transitive 280 | description: 281 | name: path_provider 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "1.6.7" 285 | path_provider_macos: 286 | dependency: transitive 287 | description: 288 | name: path_provider_macos 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "0.0.4+1" 292 | path_provider_platform_interface: 293 | dependency: transitive 294 | description: 295 | name: path_provider_platform_interface 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "1.0.1" 299 | pedantic: 300 | dependency: transitive 301 | description: 302 | name: pedantic 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "1.8.0+1" 306 | permission_handler: 307 | dependency: "direct main" 308 | description: 309 | name: permission_handler 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "5.0.1+1" 313 | permission_handler_platform_interface: 314 | dependency: transitive 315 | description: 316 | name: permission_handler_platform_interface 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "2.0.1" 320 | platform: 321 | dependency: transitive 322 | description: 323 | name: platform 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "2.2.1" 327 | plugin_platform_interface: 328 | dependency: transitive 329 | description: 330 | name: plugin_platform_interface 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "1.0.2" 334 | provider: 335 | dependency: "direct main" 336 | description: 337 | name: provider 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "4.3.2+2" 341 | quiver: 342 | dependency: transitive 343 | description: 344 | name: quiver 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "2.1.3" 348 | rxdart: 349 | dependency: "direct main" 350 | description: 351 | name: rxdart 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "0.24.1" 355 | sky_engine: 356 | dependency: transitive 357 | description: flutter 358 | source: sdk 359 | version: "0.0.99" 360 | source_span: 361 | dependency: transitive 362 | description: 363 | name: source_span 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "1.7.0" 367 | stack_trace: 368 | dependency: transitive 369 | description: 370 | name: stack_trace 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "1.9.5" 374 | stream_channel: 375 | dependency: transitive 376 | description: 377 | name: stream_channel 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "2.0.0" 381 | string_scanner: 382 | dependency: transitive 383 | description: 384 | name: string_scanner 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "1.0.5" 388 | term_glyph: 389 | dependency: transitive 390 | description: 391 | name: term_glyph 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "1.1.0" 395 | test_api: 396 | dependency: transitive 397 | description: 398 | name: test_api 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "0.2.17" 402 | typed_data: 403 | dependency: transitive 404 | description: 405 | name: typed_data 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "1.2.0" 409 | uuid: 410 | dependency: "direct main" 411 | description: 412 | name: uuid 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "2.0.4" 416 | vector_math: 417 | dependency: transitive 418 | description: 419 | name: vector_math 420 | url: "https://pub.dartlang.org" 421 | source: hosted 422 | version: "2.0.8" 423 | sdks: 424 | dart: ">=2.9.0-14.0.dev <3.0.0" 425 | flutter: ">=1.16.0 <2.0.0" 426 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3915B9972454B544000BD821 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3915B9962454B544000BD821 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 68A0E9B6A28F5130E373EEE2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE17B991E2D6714A5E1D4FF5 /* Pods_Runner.framework */; }; 14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 3915B9962454B544000BD821 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 4B20A56B8C0449CF4994B84D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 39 | 65A2F69D4D2E6DBD65A20F91 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 40 | 6CE5340600B65B28702F21B5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 41 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 42 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | FE17B991E2D6714A5E1D4FF5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 68A0E9B6A28F5130E373EEE2 /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5DD5AAD190E733AFF61C2C33 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | FE17B991E2D6714A5E1D4FF5 /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | B177B850C742BF807655567F /* Pods */, 92 | 5DD5AAD190E733AFF61C2C33 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 3915B9962454B544000BD821 /* GoogleService-Info.plist */, 108 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 109 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 110 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 111 | 97C147021CF9000F007C117D /* Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | B177B850C742BF807655567F /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 65A2F69D4D2E6DBD65A20F91 /* Pods-Runner.debug.xcconfig */, 132 | 6CE5340600B65B28702F21B5 /* Pods-Runner.release.xcconfig */, 133 | 4B20A56B8C0449CF4994B84D /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 8431FA21782795D8D0012351 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 79635FBF6A90A2364C399DB8 /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | DevelopmentTeam = K6BTRK3UFD; 175 | LastSwiftMigration = 1100; 176 | }; 177 | }; 178 | }; 179 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 180 | compatibilityVersion = "Xcode 3.2"; 181 | developmentRegion = en; 182 | hasScannedForEncodings = 0; 183 | knownRegions = ( 184 | en, 185 | Base, 186 | ); 187 | mainGroup = 97C146E51CF9000F007C117D; 188 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 189 | projectDirPath = ""; 190 | projectRoot = ""; 191 | targets = ( 192 | 97C146ED1CF9000F007C117D /* Runner */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 97C146EC1CF9000F007C117D /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 203 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 204 | 3915B9972454B544000BD821 /* GoogleService-Info.plist in Resources */, 205 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 206 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXResourcesBuildPhase section */ 211 | 212 | /* Begin PBXShellScriptBuildPhase section */ 213 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 214 | isa = PBXShellScriptBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | ); 218 | inputPaths = ( 219 | ); 220 | name = "Thin Binary"; 221 | outputPaths = ( 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | shellPath = /bin/sh; 225 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 226 | }; 227 | 79635FBF6A90A2364C399DB8 /* [CP] Embed Pods Frameworks */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "[CP] Embed Pods Frameworks"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 240 | showEnvVarsInLog = 0; 241 | }; 242 | 8431FA21782795D8D0012351 /* [CP] Check Pods Manifest.lock */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputFileListPaths = ( 248 | ); 249 | inputPaths = ( 250 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 251 | "${PODS_ROOT}/Manifest.lock", 252 | ); 253 | name = "[CP] Check Pods Manifest.lock"; 254 | outputFileListPaths = ( 255 | ); 256 | outputPaths = ( 257 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | shellPath = /bin/sh; 261 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 262 | showEnvVarsInLog = 0; 263 | }; 264 | 9740EEB61CF901F6004384FC /* Run Script */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputPaths = ( 270 | ); 271 | name = "Run Script"; 272 | outputPaths = ( 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | shellPath = /bin/sh; 276 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 277 | }; 278 | /* End PBXShellScriptBuildPhase section */ 279 | 280 | /* Begin PBXSourcesBuildPhase section */ 281 | 97C146EA1CF9000F007C117D /* Sources */ = { 282 | isa = PBXSourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 286 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin PBXVariantGroup section */ 293 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | 97C146FB1CF9000F007C117D /* Base */, 297 | ); 298 | name = Main.storyboard; 299 | sourceTree = ""; 300 | }; 301 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 97C147001CF9000F007C117D /* Base */, 305 | ); 306 | name = LaunchScreen.storyboard; 307 | sourceTree = ""; 308 | }; 309 | /* End PBXVariantGroup section */ 310 | 311 | /* Begin XCBuildConfiguration section */ 312 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_ANALYZER_NONNULL = YES; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 318 | CLANG_CXX_LIBRARY = "libc++"; 319 | CLANG_ENABLE_MODULES = YES; 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 322 | CLANG_WARN_BOOL_CONVERSION = YES; 323 | CLANG_WARN_COMMA = YES; 324 | CLANG_WARN_CONSTANT_CONVERSION = YES; 325 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 336 | CLANG_WARN_STRICT_PROTOTYPES = YES; 337 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = NO; 342 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 343 | ENABLE_NS_ASSERTIONS = NO; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | GCC_C_LANGUAGE_STANDARD = gnu99; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | SDKROOT = iphoneos; 356 | SUPPORTED_PLATFORMS = iphoneos; 357 | TARGETED_DEVICE_FAMILY = "1,2"; 358 | VALIDATE_PRODUCT = YES; 359 | }; 360 | name = Profile; 361 | }; 362 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | CLANG_ENABLE_MODULES = YES; 368 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 369 | DEVELOPMENT_TEAM = K6BTRK3UFD; 370 | ENABLE_BITCODE = NO; 371 | FRAMEWORK_SEARCH_PATHS = ( 372 | "$(inherited)", 373 | "$(PROJECT_DIR)/Flutter", 374 | ); 375 | INFOPLIST_FILE = Runner/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 377 | LIBRARY_SEARCH_PATHS = ( 378 | "$(inherited)", 379 | "$(PROJECT_DIR)/Flutter", 380 | ); 381 | PRODUCT_BUNDLE_IDENTIFIER = com.julow.farmersMarket; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 384 | SWIFT_VERSION = 5.0; 385 | VERSIONING_SYSTEM = "apple-generic"; 386 | }; 387 | name = Profile; 388 | }; 389 | 97C147031CF9000F007C117D /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_SEARCH_USER_PATHS = NO; 393 | CLANG_ANALYZER_NONNULL = YES; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 399 | CLANG_WARN_BOOL_CONVERSION = YES; 400 | CLANG_WARN_COMMA = YES; 401 | CLANG_WARN_CONSTANT_CONVERSION = YES; 402 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 403 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 404 | CLANG_WARN_EMPTY_BODY = YES; 405 | CLANG_WARN_ENUM_CONVERSION = YES; 406 | CLANG_WARN_INFINITE_RECURSION = YES; 407 | CLANG_WARN_INT_CONVERSION = YES; 408 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = dwarf; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | ENABLE_TESTABILITY = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_DYNAMIC_NO_PIC = NO; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_OPTIMIZATION_LEVEL = 0; 426 | GCC_PREPROCESSOR_DEFINITIONS = ( 427 | "DEBUG=1", 428 | "$(inherited)", 429 | ); 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 437 | MTL_ENABLE_DEBUG_INFO = YES; 438 | ONLY_ACTIVE_ARCH = YES; 439 | SDKROOT = iphoneos; 440 | TARGETED_DEVICE_FAMILY = "1,2"; 441 | }; 442 | name = Debug; 443 | }; 444 | 97C147041CF9000F007C117D /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_ANALYZER_NONNULL = YES; 449 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 450 | CLANG_CXX_LIBRARY = "libc++"; 451 | CLANG_ENABLE_MODULES = YES; 452 | CLANG_ENABLE_OBJC_ARC = YES; 453 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 454 | CLANG_WARN_BOOL_CONVERSION = YES; 455 | CLANG_WARN_COMMA = YES; 456 | CLANG_WARN_CONSTANT_CONVERSION = YES; 457 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_EMPTY_BODY = YES; 460 | CLANG_WARN_ENUM_CONVERSION = YES; 461 | CLANG_WARN_INFINITE_RECURSION = YES; 462 | CLANG_WARN_INT_CONVERSION = YES; 463 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 465 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 467 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 468 | CLANG_WARN_STRICT_PROTOTYPES = YES; 469 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 475 | ENABLE_NS_ASSERTIONS = NO; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | GCC_C_LANGUAGE_STANDARD = gnu99; 478 | GCC_NO_COMMON_BLOCKS = YES; 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 486 | MTL_ENABLE_DEBUG_INFO = NO; 487 | SDKROOT = iphoneos; 488 | SUPPORTED_PLATFORMS = iphoneos; 489 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 490 | TARGETED_DEVICE_FAMILY = "1,2"; 491 | VALIDATE_PRODUCT = YES; 492 | }; 493 | name = Release; 494 | }; 495 | 97C147061CF9000F007C117D /* Debug */ = { 496 | isa = XCBuildConfiguration; 497 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 498 | buildSettings = { 499 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 500 | CLANG_ENABLE_MODULES = YES; 501 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 502 | DEVELOPMENT_TEAM = K6BTRK3UFD; 503 | ENABLE_BITCODE = NO; 504 | FRAMEWORK_SEARCH_PATHS = ( 505 | "$(inherited)", 506 | "$(PROJECT_DIR)/Flutter", 507 | ); 508 | INFOPLIST_FILE = Runner/Info.plist; 509 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 510 | LIBRARY_SEARCH_PATHS = ( 511 | "$(inherited)", 512 | "$(PROJECT_DIR)/Flutter", 513 | ); 514 | PRODUCT_BUNDLE_IDENTIFIER = com.julow.farmersMarket; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 517 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 518 | SWIFT_VERSION = 5.0; 519 | VERSIONING_SYSTEM = "apple-generic"; 520 | }; 521 | name = Debug; 522 | }; 523 | 97C147071CF9000F007C117D /* Release */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 526 | buildSettings = { 527 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 528 | CLANG_ENABLE_MODULES = YES; 529 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 530 | DEVELOPMENT_TEAM = K6BTRK3UFD; 531 | ENABLE_BITCODE = NO; 532 | FRAMEWORK_SEARCH_PATHS = ( 533 | "$(inherited)", 534 | "$(PROJECT_DIR)/Flutter", 535 | ); 536 | INFOPLIST_FILE = Runner/Info.plist; 537 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 538 | LIBRARY_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "$(PROJECT_DIR)/Flutter", 541 | ); 542 | PRODUCT_BUNDLE_IDENTIFIER = com.julow.farmersMarket; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 545 | SWIFT_VERSION = 5.0; 546 | VERSIONING_SYSTEM = "apple-generic"; 547 | }; 548 | name = Release; 549 | }; 550 | /* End XCBuildConfiguration section */ 551 | 552 | /* Begin XCConfigurationList section */ 553 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | 97C147031CF9000F007C117D /* Debug */, 557 | 97C147041CF9000F007C117D /* Release */, 558 | 249021D3217E4FDB00AE95B9 /* Profile */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 97C147061CF9000F007C117D /* Debug */, 567 | 97C147071CF9000F007C117D /* Release */, 568 | 249021D4217E4FDB00AE95B9 /* Profile */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | defaultConfigurationName = Release; 572 | }; 573 | /* End XCConfigurationList section */ 574 | }; 575 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 576 | } 577 | --------------------------------------------------------------------------------