Loading lib/pages/create/create_back/create_back_page.dart +170 −77 Original line number Diff line number Diff line import 'package:country_picker/country_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:stamped/bottom_nav_bar/widget/bottom_nav_bar.dart'; import 'package:stamped/common/service/ioc_container.dart'; import 'package:stamped/common/util/shared_ui_constants.dart'; import 'package:stamped/common/widget/future_builder_handler.dart'; import 'package:stamped/common/widget/page_template.dart'; import 'package:stamped/pages/create/notifiers/create_postcard_notifier.dart'; import 'package:stamped/service/auth_service.dart'; Loading Loading @@ -38,24 +41,9 @@ class _CreateBackPageState extends State<CreateBackPage> { return PageTemplate( title: 'Create a Postcard', actions: [ IconButton( onPressed: () { createPostcardNotifier.text = _textEditingController.text; // TODO: select destination country }, icon: Icon(Icons.navigate_next), ), _buildSubmitButton(createPostcardNotifier, context), ], child: Container( decoration: BoxDecoration( color: Colors.white, border: Border.all( color: Colors.grey, width: 2, ), ), padding: EdgeInsets.all(STANDARD_GAP), height: double.infinity, child: _buildPostcardBackground( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Loading @@ -65,8 +53,24 @@ class _CreateBackPageState extends State<CreateBackPage> { color: Colors.black87, ), Spacer(), _buildGreetingsText(), _buildLocationText(createPostcardNotifier), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildFadedText('Greetings from'), _buildFadedText('To:') ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // _buildBoldText(createPostcardNotifier), FutureBuilderHandler( future: createPostcardNotifier.location, builder: (context, data) => _buildBoldText(data), ), _buildDestinationSelector(context, createPostcardNotifier), ], ), Spacer(), _buildFooter( createPostcardNotifier, Loading @@ -79,47 +83,139 @@ class _CreateBackPageState extends State<CreateBackPage> { ); } Widget _buildTextField() { return TextField( IconButton _buildSubmitButton( CreatePostcardNotifier createPostcardNotifier, BuildContext context, ) => IconButton( onPressed: () { if (_textEditingController.text == '') { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please enter a message.', ), ), ); return; } if (createPostcardNotifier.destination == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please select a destination country.', ), ), ); return; } createPostcardNotifier.text = _textEditingController.text; try { createPostcardNotifier.createPostcard(); } on Exception catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( e.toString(), ), ), ); } Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => BottomNavBar(), ), ); }, icon: Icon(Icons.check), ); Widget _buildPostcardBackground({required Widget child}) => Container( decoration: BoxDecoration( color: Colors.white, border: Border.all( color: Colors.grey, width: 2, ), ), padding: EdgeInsets.all(STANDARD_GAP), height: double.infinity, child: child, ); Widget _buildDestinationSelector( BuildContext context, CreatePostcardNotifier createPostcardNotifier, ) { final isNull = createPostcardNotifier.destination == null; return InkWell( onTap: () { showCountryPicker( context: context, onSelect: (Country country) { createPostcardNotifier.destination = country; }, ); }, child: isNull ? Icon( Icons.cancel_presentation_sharp, color: Colors.black45, ) : Text( createPostcardNotifier.destination?.flagEmoji ?? 'Select a destination', style: TextStyle( fontSize: FONT_SIZE, ), ), ); } Widget _buildTextField() => TextField( maxLength: 220, maxLines: 12, style: TextStyle( fontSize: FONT_SIZE, ), decoration: InputDecoration( hintText: 'Type a message', border: InputBorder.none, ), controller: _textEditingController, ); } Widget _buildGreetingsText() { return Text( 'Greetings from', Widget _buildFadedText(String text) => Text( text, style: TextStyle( color: Colors.grey, fontSize: FONT_SIZE, fontWeight: FontWeight.w600, ), ); } Widget _buildLocationText(CreatePostcardNotifier notifier) { return Text( notifier.location, Widget _buildBoldText(String text) => Text( text, style: TextStyle( color: Colors.black, fontSize: FONT_SIZE, fontWeight: FontWeight.w800, ), ); } Widget _buildFooter( CreatePostcardNotifier notifier, AuthService authService, ) { return Row( ) => Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Loading @@ -136,10 +232,8 @@ class _CreateBackPageState extends State<CreateBackPage> { _buildSignature(authService), ], ); } Widget _buildSignature(AuthService authService) { return Expanded( Widget _buildSignature(AuthService authService) => Expanded( child: Text( textAlign: TextAlign.right, authService.currentUser.userName, Loading @@ -150,4 +244,3 @@ class _CreateBackPageState extends State<CreateBackPage> { ), ); } } lib/pages/create/notifiers/create_postcard_notifier.dart +62 −14 Original line number Diff line number Diff line import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:country_picker/country_picker.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:stamped/common/service/ioc_container.dart'; import 'package:stamped/data/entities/postcard_entity.dart'; import 'package:stamped/data/enums/image_type.dart'; import 'package:stamped/service/auth_service.dart'; import 'package:stamped/service/media_service.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:stamped/service/postcard_service.dart'; import 'package:stamped/service/user_service.dart'; class CreatePostcardNotifier extends ChangeNotifier { final _mediaService = get<MediaService>(); final _userService = get<UserService>(); final _postcardService = get<PostcardService>(); final _authService = get<AuthService>(); File? _postcardImage; String _postcardImageDownloadUrl = ''; String text = ''; final _mediaService = get<MediaService>(); Country? _destination; File? get selectedImage => _postcardImage; String get postcardImageUrl => _postcardImageDownloadUrl; String get location => 'Germany'; // TODO String get stampUrl => 'https://firebasestorage.googleapis.com/v0/b/stamped-56619.appspot.com/o/stamps%2Fczech%20republic%2FPampeliska.jpg?alt=media&token=1e100348-e8cb-4361-b1e3-37b5037491f8'; Future<String> get location async => await _userService.getCurrentDeviceLocation(); Country? get destination => _destination; set destination(Country? destination) { _destination = destination; notifyListeners(); } Future<void> pickImage(ImageSource source) async { final pickedFile = await ImagePicker().pickImage(source: source); Loading Loading @@ -57,16 +71,50 @@ class CreatePostcardNotifier extends ChangeNotifier { } Future<void> uploadPostcardImage() async { try { final imageRef = await _mediaService.uploadImage( _postcardImage!, imageType: ImageType.postcard, ); if (imageRef == null) { _postcardImageDownloadUrl = ''; return; _postcardImageDownloadUrl = await imageRef!.getDownloadURL(); } on FirebaseException catch (_) { rethrow; } } Future<void> createPostcard() async { await uploadPostcardImage(); _postcardImageDownloadUrl = await imageRef.getDownloadURL(); final allCountryStamps = await _mediaService.getStampUrls( await location.then( (value) => value.toLowerCase(), ), ); allCountryStamps.shuffle(); final stampUrl = await allCountryStamps.first; _postcardService.create( PostcardEntity( id: '0', imagePath: _postcardImageDownloadUrl, stampPath: stampUrl, originLocation: await location, destinationLocation: _destination!.name, text: text, senderId: await _authService.userStream.first.then((value) => value!.uid), timestamp: Timestamp.now(), ), ); _clear(); } void _clear() { _postcardImage = null; _postcardImageDownloadUrl = ''; text = ''; _destination = null; } } Loading
lib/pages/create/create_back/create_back_page.dart +170 −77 Original line number Diff line number Diff line import 'package:country_picker/country_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:stamped/bottom_nav_bar/widget/bottom_nav_bar.dart'; import 'package:stamped/common/service/ioc_container.dart'; import 'package:stamped/common/util/shared_ui_constants.dart'; import 'package:stamped/common/widget/future_builder_handler.dart'; import 'package:stamped/common/widget/page_template.dart'; import 'package:stamped/pages/create/notifiers/create_postcard_notifier.dart'; import 'package:stamped/service/auth_service.dart'; Loading Loading @@ -38,24 +41,9 @@ class _CreateBackPageState extends State<CreateBackPage> { return PageTemplate( title: 'Create a Postcard', actions: [ IconButton( onPressed: () { createPostcardNotifier.text = _textEditingController.text; // TODO: select destination country }, icon: Icon(Icons.navigate_next), ), _buildSubmitButton(createPostcardNotifier, context), ], child: Container( decoration: BoxDecoration( color: Colors.white, border: Border.all( color: Colors.grey, width: 2, ), ), padding: EdgeInsets.all(STANDARD_GAP), height: double.infinity, child: _buildPostcardBackground( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Loading @@ -65,8 +53,24 @@ class _CreateBackPageState extends State<CreateBackPage> { color: Colors.black87, ), Spacer(), _buildGreetingsText(), _buildLocationText(createPostcardNotifier), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildFadedText('Greetings from'), _buildFadedText('To:') ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // _buildBoldText(createPostcardNotifier), FutureBuilderHandler( future: createPostcardNotifier.location, builder: (context, data) => _buildBoldText(data), ), _buildDestinationSelector(context, createPostcardNotifier), ], ), Spacer(), _buildFooter( createPostcardNotifier, Loading @@ -79,47 +83,139 @@ class _CreateBackPageState extends State<CreateBackPage> { ); } Widget _buildTextField() { return TextField( IconButton _buildSubmitButton( CreatePostcardNotifier createPostcardNotifier, BuildContext context, ) => IconButton( onPressed: () { if (_textEditingController.text == '') { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please enter a message.', ), ), ); return; } if (createPostcardNotifier.destination == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please select a destination country.', ), ), ); return; } createPostcardNotifier.text = _textEditingController.text; try { createPostcardNotifier.createPostcard(); } on Exception catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( e.toString(), ), ), ); } Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => BottomNavBar(), ), ); }, icon: Icon(Icons.check), ); Widget _buildPostcardBackground({required Widget child}) => Container( decoration: BoxDecoration( color: Colors.white, border: Border.all( color: Colors.grey, width: 2, ), ), padding: EdgeInsets.all(STANDARD_GAP), height: double.infinity, child: child, ); Widget _buildDestinationSelector( BuildContext context, CreatePostcardNotifier createPostcardNotifier, ) { final isNull = createPostcardNotifier.destination == null; return InkWell( onTap: () { showCountryPicker( context: context, onSelect: (Country country) { createPostcardNotifier.destination = country; }, ); }, child: isNull ? Icon( Icons.cancel_presentation_sharp, color: Colors.black45, ) : Text( createPostcardNotifier.destination?.flagEmoji ?? 'Select a destination', style: TextStyle( fontSize: FONT_SIZE, ), ), ); } Widget _buildTextField() => TextField( maxLength: 220, maxLines: 12, style: TextStyle( fontSize: FONT_SIZE, ), decoration: InputDecoration( hintText: 'Type a message', border: InputBorder.none, ), controller: _textEditingController, ); } Widget _buildGreetingsText() { return Text( 'Greetings from', Widget _buildFadedText(String text) => Text( text, style: TextStyle( color: Colors.grey, fontSize: FONT_SIZE, fontWeight: FontWeight.w600, ), ); } Widget _buildLocationText(CreatePostcardNotifier notifier) { return Text( notifier.location, Widget _buildBoldText(String text) => Text( text, style: TextStyle( color: Colors.black, fontSize: FONT_SIZE, fontWeight: FontWeight.w800, ), ); } Widget _buildFooter( CreatePostcardNotifier notifier, AuthService authService, ) { return Row( ) => Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Loading @@ -136,10 +232,8 @@ class _CreateBackPageState extends State<CreateBackPage> { _buildSignature(authService), ], ); } Widget _buildSignature(AuthService authService) { return Expanded( Widget _buildSignature(AuthService authService) => Expanded( child: Text( textAlign: TextAlign.right, authService.currentUser.userName, Loading @@ -150,4 +244,3 @@ class _CreateBackPageState extends State<CreateBackPage> { ), ); } }
lib/pages/create/notifiers/create_postcard_notifier.dart +62 −14 Original line number Diff line number Diff line import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:country_picker/country_picker.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:stamped/common/service/ioc_container.dart'; import 'package:stamped/data/entities/postcard_entity.dart'; import 'package:stamped/data/enums/image_type.dart'; import 'package:stamped/service/auth_service.dart'; import 'package:stamped/service/media_service.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:stamped/service/postcard_service.dart'; import 'package:stamped/service/user_service.dart'; class CreatePostcardNotifier extends ChangeNotifier { final _mediaService = get<MediaService>(); final _userService = get<UserService>(); final _postcardService = get<PostcardService>(); final _authService = get<AuthService>(); File? _postcardImage; String _postcardImageDownloadUrl = ''; String text = ''; final _mediaService = get<MediaService>(); Country? _destination; File? get selectedImage => _postcardImage; String get postcardImageUrl => _postcardImageDownloadUrl; String get location => 'Germany'; // TODO String get stampUrl => 'https://firebasestorage.googleapis.com/v0/b/stamped-56619.appspot.com/o/stamps%2Fczech%20republic%2FPampeliska.jpg?alt=media&token=1e100348-e8cb-4361-b1e3-37b5037491f8'; Future<String> get location async => await _userService.getCurrentDeviceLocation(); Country? get destination => _destination; set destination(Country? destination) { _destination = destination; notifyListeners(); } Future<void> pickImage(ImageSource source) async { final pickedFile = await ImagePicker().pickImage(source: source); Loading Loading @@ -57,16 +71,50 @@ class CreatePostcardNotifier extends ChangeNotifier { } Future<void> uploadPostcardImage() async { try { final imageRef = await _mediaService.uploadImage( _postcardImage!, imageType: ImageType.postcard, ); if (imageRef == null) { _postcardImageDownloadUrl = ''; return; _postcardImageDownloadUrl = await imageRef!.getDownloadURL(); } on FirebaseException catch (_) { rethrow; } } Future<void> createPostcard() async { await uploadPostcardImage(); _postcardImageDownloadUrl = await imageRef.getDownloadURL(); final allCountryStamps = await _mediaService.getStampUrls( await location.then( (value) => value.toLowerCase(), ), ); allCountryStamps.shuffle(); final stampUrl = await allCountryStamps.first; _postcardService.create( PostcardEntity( id: '0', imagePath: _postcardImageDownloadUrl, stampPath: stampUrl, originLocation: await location, destinationLocation: _destination!.name, text: text, senderId: await _authService.userStream.first.then((value) => value!.uid), timestamp: Timestamp.now(), ), ); _clear(); } void _clear() { _postcardImage = null; _postcardImageDownloadUrl = ''; text = ''; _destination = null; } }