import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'place.dart'; import 'stub_data.dart'; class PlaceDetails extends StatefulWidget { const PlaceDetails({ @required this.place, @required this.onChanged, Key key, }) : assert(place != null), assert(onChanged != null), super(key: key); final Place place; final ValueChanged onChanged; @override PlaceDetailsState createState() => PlaceDetailsState(); } class PlaceDetailsState extends State { Place _place; GoogleMapController _mapController; final Set _markers = {}; final TextEditingController _nameController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController(); @override void initState() { _place = widget.place; _nameController.text = _place.name; _descriptionController.text = _place.description; return super.initState(); } void _onMapCreated(GoogleMapController controller) { _mapController = controller; setState(() { _markers.add(Marker( markerId: MarkerId(_place.latLng.toString()), position: _place.latLng, )); }); } Widget _detailsBody() { return ListView( padding: const EdgeInsets.fromLTRB(24.0, 12.0, 24.0, 12.0), children: [ _NameTextField( controller: _nameController, onChanged: (value) { setState(() { _place = _place.copyWith(name: value); }); }, ), _DescriptionTextField( controller: _descriptionController, onChanged: (value) { setState(() { _place = _place.copyWith(description: value); }); }, ), _StarBar( rating: _place.starRating, onChanged: (value) { setState(() { _place = _place.copyWith(starRating: value); }); }, ), _Map( center: _place.latLng, mapController: _mapController, onMapCreated: _onMapCreated, markers: _markers, ), const _Reviews(), ], ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('${_place.name}'), backgroundColor: Colors.green[700], actions: [ Padding( padding: const EdgeInsets.fromLTRB(0.0, 0.0, 8.0, 0.0), child: IconButton( icon: const Icon(Icons.save, size: 30.0), onPressed: () { widget.onChanged(_place); Navigator.pop(context); }, ), ), ], ), body: GestureDetector( onTap: () { FocusScope.of(context).requestFocus(FocusNode()); }, child: _detailsBody(), ), ); } } class _NameTextField extends StatelessWidget { const _NameTextField({ @required this.controller, @required this.onChanged, Key key, }) : assert(controller != null), assert(onChanged != null), super(key: key); final TextEditingController controller; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), child: TextField( decoration: const InputDecoration( labelText: 'Name', labelStyle: TextStyle(fontSize: 18.0), ), style: const TextStyle(fontSize: 20.0, color: Colors.black87), autocorrect: true, controller: controller, onChanged: (value) { onChanged(value); }, ), ); } } class _DescriptionTextField extends StatelessWidget { const _DescriptionTextField({ @required this.controller, @required this.onChanged, Key key, }) : assert(controller != null), assert(onChanged != null), super(key: key); final TextEditingController controller; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 16.0), child: TextField( decoration: const InputDecoration( labelText: 'Description', labelStyle: TextStyle(fontSize: 18.0), ), style: const TextStyle(fontSize: 20.0, color: Colors.black87), maxLines: null, autocorrect: true, controller: controller, onChanged: (value) { onChanged(value); }, ), ); } } class _StarBar extends StatelessWidget { const _StarBar({ @required this.rating, @required this.onChanged, Key key, }) : assert(rating != null && rating >= 0 && rating <= maxStars), assert(onChanged != null), super(key: key); static const int maxStars = 5; final int rating; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(maxStars, (index) { return IconButton( icon: const Icon(Icons.star), iconSize: 40.0, color: rating > index ? Colors.amber : Colors.grey[400], onPressed: () { onChanged(index + 1); }, ); }).toList(), ); } } class _Map extends StatelessWidget { const _Map({ @required this.center, @required this.mapController, @required this.onMapCreated, @required this.markers, Key key, }) : assert(center != null), assert(onMapCreated != null), super(key: key); final LatLng center; final GoogleMapController mapController; final ArgumentCallback onMapCreated; final Set markers; @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.symmetric(vertical: 16.0), elevation: 4.0, child: SizedBox( width: 340.0, height: 240.0, child: GoogleMap( onMapCreated: onMapCreated, initialCameraPosition: CameraPosition( target: center, zoom: 16.0, ), markers: markers, zoomGesturesEnabled: false, rotateGesturesEnabled: false, tiltGesturesEnabled: false, scrollGesturesEnabled: false, ), ), ); } } class _Reviews extends StatelessWidget { const _Reviews({ Key key, }) : super(key: key); Widget _buildSingleReview(String reviewText) { return Column( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 10.0), child: Row( children: [ Container( width: 80.0, height: 80.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(40.0), border: Border.all( width: 3.0, color: Colors.grey, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Text( '5', style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Icon( Icons.star, color: Colors.amber, size: 36.0, ), ], ), ), const SizedBox(width: 16.0), Expanded( child: Text( reviewText, style: const TextStyle(fontSize: 20.0, color: Colors.black87), maxLines: null, ), ), ], ), ), Divider( height: 8.0, color: Colors.grey[700], ), ], ); } @override Widget build(BuildContext context) { return Column( children: [ const Padding( padding: EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 8.0), child: Align( alignment: Alignment.topLeft, child: Text( 'Reviews', style: TextStyle( fontSize: 24.0, fontWeight: FontWeight.bold, decoration: TextDecoration.underline, color: Colors.black87, ), ), ), ), Column( children: StubData.reviewStrings .map((reviewText) => _buildSingleReview(reviewText)) .toList(), ), ], ); } }