Add list view to place tracker. Note: map in listTile is not WAI in this commit.

pull/30/head
kenzieschmoll 8 years ago
parent 7ab277fad4
commit b95215fb00

@ -0,0 +1,216 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'place.dart';
class PlaceList extends StatefulWidget {
const PlaceList({
@required this.places,
@required this.selectedCategory,
@required this.onCategoryChanged,
Key key,
}) : assert(places != null),
super(key: key);
final List<Place> places;
final PlaceCategory selectedCategory;
final ValueChanged<PlaceCategory> onCategoryChanged;
@override
PlaceListState createState() => PlaceListState();
}
class PlaceListState extends State<PlaceList> {
ScrollController _scrollController = ScrollController();
void _onCategoryChanged(PlaceCategory newCategory) {
_scrollController.jumpTo(0.0);
widget.onCategoryChanged(newCategory);
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
_ListCategoryButtonBar(
selectedCategory: widget.selectedCategory,
onCategoryChanged: (value) => _onCategoryChanged(value),
),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
controller: _scrollController,
shrinkWrap: true,
children: widget.places.map((Place place) => _PlaceListTile(place: place)).toList(),
),
),
],
);
}
}
class _PlaceListTile extends StatelessWidget {
const _PlaceListTile({
Key key,
@required this.place
}) : assert(place != null),
super(key: key);
final Place place;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
place.name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
maxLines: 3,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: List.generate(5, (int index) {
return Icon(Icons.star,
size: 28.0,
color: place.starRating > index ? Colors.amber : Colors.grey[400],
);
}).toList(),
),
Text(
place.description != null ? place.description : '',
style: Theme.of(context).textTheme.subhead,
maxLines: 4,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
_Map(center: place.latLng),
],
),
Divider(
height: 36.0,
color: Colors.grey[700],
),
],
);
}
}
class _Map extends StatelessWidget {
const _Map({
@required this.center,
Key key,
}) : assert(center != null),
super(key: key);
final LatLng center;
_onMapCreated(GoogleMapController controller) {
controller.addMarker(
MarkerOptions(
position: center,
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 140.0,
width: 160.0,
child: GoogleMap(
onMapCreated: _onMapCreated,
options: GoogleMapOptions(
cameraPosition: CameraPosition(
target: center,
zoom: 17.0,
),
zoomGesturesEnabled: false,
rotateGesturesEnabled: false,
tiltGesturesEnabled: false,
scrollGesturesEnabled: false,
),
),
);
}
}
class _ListCategoryButtonBar extends StatelessWidget {
const _ListCategoryButtonBar({
@required this.selectedCategory,
@required this.onCategoryChanged,
Key key,
})
: assert(selectedCategory != null),
assert(onCategoryChanged != null),
super(key: key);
final PlaceCategory selectedCategory;
final ValueChanged<PlaceCategory> onCategoryChanged;
Widget _buildButtonForCategory(PlaceCategory category) {
String _buttonText;
switch (category) {
case PlaceCategory.favorite:
_buttonText = 'Favorites';
break;
case PlaceCategory.visited:
_buttonText = 'Visited';
break;
case PlaceCategory.wantToGo:
_buttonText = 'Want To Go';
}
return Container(
margin: EdgeInsets.symmetric(vertical: 12.0),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: selectedCategory == category ? Colors.blue : Colors.transparent,
),
),
),
child: ButtonTheme(
height: 50.0,
child: FlatButton(
child: Text(
_buttonText,
style: TextStyle(
fontSize: selectedCategory == category ? 20.0 : 18.0,
color: selectedCategory == category ? Colors.blue : Colors.black87,
),
),
onPressed: () => onCategoryChanged(category),
),
),
);
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildButtonForCategory(PlaceCategory.favorite),
_buildButtonForCategory(PlaceCategory.visited),
_buildButtonForCategory(PlaceCategory.wantToGo),
],
);
}
}

@ -6,6 +6,12 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'place.dart';
import 'place_stub_data.dart';
import 'place_details.dart';
import 'place_list.dart';
enum PlaceTrackerViewType {
map,
list,
}
class PlaceMap extends StatefulWidget {
const PlaceMap({
@ -43,9 +49,10 @@ class PlaceMapState extends State<PlaceMap> {
}
GoogleMapController mapController;
PlaceCategory _selectedPlaceCategory = PlaceCategory.favorite;
Map<Marker, Place> _places = Map<Marker, Place>();
Marker _pendingMarker;
Map<Marker, Place> _places = Map<Marker, Place>();
PlaceCategory _selectedPlaceCategory = PlaceCategory.favorite;
PlaceTrackerViewType viewType = PlaceTrackerViewType.map;
void onMapCreated(GoogleMapController controller) async {
mapController = controller;
@ -122,7 +129,7 @@ class PlaceMapState extends State<PlaceMap> {
);
}
void _updatePlaces(PlaceCategory category) {
void _updateDisplayedPlaces(PlaceCategory category) {
setState(() {
_selectedPlaceCategory = category;
_showPlacesForSelectedCategory();
@ -234,6 +241,72 @@ class PlaceMapState extends State<PlaceMap> {
}
}
void _switchViewType() {
switch (viewType) {
case PlaceTrackerViewType.map:
setState(() {
viewType = PlaceTrackerViewType.list;
});
break;
case PlaceTrackerViewType.list:
setState(() {
viewType = PlaceTrackerViewType.map;
});
}
}
Widget _placeMap() {
return Builder(builder: (BuildContext context) {
// We need this additional builder here so that we can pass its context to
// _AddPlaceButtonBar's onSavePressed callback. This callback shows a
// SnackBar and to do this, we need a build context that has Scaffold as
// an ancestor.
return Center(
child: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: onMapCreated,
options: GoogleMapOptions(
trackCameraPosition: true,
cameraPosition: CameraPosition(
target: widget.center,
zoom: 11.0,
),
),
),
_CategoryButtonBar(
selectedPlaceCategory: _selectedPlaceCategory,
visible: _pendingMarker == null,
onChanged: _updateDisplayedPlaces,
),
_AddPlaceButtonBar(
visible: _pendingMarker != null,
onSavePressed: () => _confirmAddPlace(context),
onCancelPressed: _cancelAddPlace,
),
_AddPlaceFab(
visible: _pendingMarker == null,
onPressed: _onAddPlaceFabPressed,
),
],
),
);
});
}
Widget _placeList() {
return PlaceList(
places: _getPlacesForCategory(_selectedPlaceCategory, _places),
selectedCategory: _selectedPlaceCategory,
onCategoryChanged: (PlaceCategory value) {
setState(() {
_selectedPlaceCategory = value;
_updateDisplayedPlaces(_selectedPlaceCategory);
});
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -249,43 +322,28 @@ class PlaceMapState extends State<PlaceMap> {
],
),
backgroundColor: Colors.green[700],
),
// We need this additional builder here so that we can pass its context to
// _AddPlaceButtonBar's onSavePressed callback. This callback shows a
// SnackBar and to do this, we need a build context that has Scaffold as
// an ancestor.
body: Builder(builder: (BuildContext context) {
return Center(
child: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: onMapCreated,
options: GoogleMapOptions(
trackCameraPosition: true,
cameraPosition: CameraPosition(
target: widget.center,
zoom: 11.0,
),
),
),
_CategoryButtonBar(
selectedPlaceCategory: _selectedPlaceCategory,
visible: _pendingMarker == null,
onChanged: _updatePlaces,
actions: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0.0, 0.0, 16.0, 0.0),
child: IconButton(
icon: Icon(
viewType == PlaceTrackerViewType.map
? Icons.list
: Icons.map,
size: 32.0
),
_AddPlaceButtonBar(
visible: _pendingMarker != null,
onSavePressed: () => _confirmAddPlace(context),
onCancelPressed: _cancelAddPlace,
),
_AddPlaceFab(
visible: _pendingMarker == null,
onPressed: _onAddPlaceFabPressed,
),
],
onPressed: _switchViewType,
),
),
);
}),
],
),
body: IndexedStack(
index: viewType == PlaceTrackerViewType.map ? 0 : 1,
children: <Widget>[
_placeMap(),
_placeList(),
],
),
);
}
}
@ -417,7 +475,7 @@ class _AddPlaceFab extends StatelessWidget {
return Opacity(
opacity: visible ? 1.0 : 0.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(12.0),
child: Align(
alignment: Alignment.topRight,
child: FloatingActionButton(
@ -430,4 +488,4 @@ class _AddPlaceFab extends StatelessWidget {
),
);
}
}
}

Loading…
Cancel
Save