In this lesson we'll cover:
You can check out the step/step08
branch here which will contain the code for this lesson.
// lib/models/location.dart
import './location_fact.dart';
import 'package:meta/meta.dart';
/// Represents a tourism location a user can visit.
class Location {
final int id;
final String name;
final String imagePath;
final String userItinerarySummary;
final String tourPackageName;
final List<LocationFact> facts;
Location({
this.id,
this.name,
this.imagePath,
this.userItinerarySummary,
this.tourPackageName,
this.facts,
});
static List<Location> fetchAll() {
return [
Location(
id: 1,
name: 'Kiyomizu-dera',
imagePath: 'assets/images/kiyomizu-dera.jpg',
userItinerarySummary: 'Day 1: 4PM - 5:00PM',
tourPackageName: 'Standard Package',
facts: [
LocationFact('Summary',
'Kiyomizu-dera, officially Otowa-san Kiyomizu-dera, is an independent Buddhist temple in eastern Kyoto. The temple is part of the Historic Monuments of Ancient Kyoto UNESCO World Heritage site.'),
LocationFact(
'Architectural Style', 'Japanese Buddhist architecture.'),
]),
Location(
id: 2,
name: 'Mount Fuji',
imagePath: 'assets/images/fuji.jpg',
userItinerarySummary: 'Day 1: 9AM - 1:30PM',
tourPackageName: 'Standard Package',
facts: [
LocationFact('Summary',
'Japan’s Mt. Fuji is an active volcano about 100 kilometers southwest of Tokyo. Commonly called “Fuji-san,” it’s the country’s tallest peak, at 3,776 meters. A pilgrimage site for centuries, it’s considered one of Japan’s 3 sacred mountains, and summit hikes remain a popular activity. Its iconic profile is the subject of numerous works of art, notably Edo Period prints by Hokusai and Hiroshige.'),
LocationFact('Did You Know',
'There are three cities that surround Mount Fuji: Gotemba, Fujiyoshida and Fujinomiya.'),
]),
Location(
id: 3,
name: 'Arashiyama Bamboo Grove',
imagePath: 'assets/images/arashiyama.jpg',
userItinerarySummary: 'Day 1: 2PM - 3:30PM',
tourPackageName: 'Standard Package',
facts: [
LocationFact('Summary',
'While we could go on about the ethereal glow and seemingly endless heights of this bamboo grove on the outskirts of Kyoto, the sight\'s pleasures extend beyond the visual realm.'),
LocationFact('How to Get There',
'Kyoto airport, with several terminals, is located 16 kilometres south of the city and is also known as Kyoto. Kyoto can also be reached by transport links from other regional airports.'),
]),
];
}
static Location fetchByID(int locationID) {
// NOTE: this will replaced by a proper API call
List<Location> locations = Location.fetchAll();
for (var i = 0; i < locations.length; i++) {
if (locations[i].id == locationID) {
return locations[i];
}
}
return null;
}
}
ListView.builder()
ListView.builder()
named constructor. Let's do that like so:// lib/screens/locations/locations.dart
// ...
return Scaffold(
// ...
body: ListView.builder(
itemCount: locations.length,
itemBuilder: (context, index) =>
_itemBuilder(context, locations[index]),
),
);
// ...
itemBuilder
// ...
// lib/screens/locations/locations.dart
// ...
Widget _itemBuilder(BuildContext context, Location location) {
return GestureDetector(
onTap: () => _onLocationTap(context, location.id),
child: Container(
height: 245.0,
child: Stack(
children: [
ImageBanner(assetPath: location.imagePath, height: 245.0),
TileOverlay(location),
],
),
),
);
}
// ...
LocationTile
Widget in LocationDetail
SingleChildScrollView
with a LocationTile
, which is already used by another widget, TileOverlay
.LocationDetail
screen's Scaffold
implementation with:// lib/screens/location_detail/location_detail.dart
// ...
return Scaffold(
// ...
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ImageBanner(assetPath: location.imagePath),
Padding(
padding:
EdgeInsets.symmetric(vertical: 20.0, horizontal: 4.0),
child: LocationTile(location: location),
),
]..addAll(textSections(location))),
));
// ...
Padding
widget, allowing us to provide some breathing room to our top section of the screen.SingleChildScrollView
allows us to easily scroll if the context goes beyond the bounds of the screen.lib/style.dart
file.// lib/style.dart
import 'package:flutter/material.dart';
const LargeTextSize = 22.0;
const MediumTextSize = 16.0;
const SmallTextSize = 12.0;
const String FontNameDefault = 'Montserrat';
const Color TextColorDark = Colors.black;
const Color TextColorLight = Colors.white;
const Color TextColorAccent = Colors.red;
const Color TextColorFaint = Color.fromRGBO(125, 125, 125, 1.0);
const DefaultPaddingHorizontal = 12.0;
const AppBarTextStyle = TextStyle(
fontFamily: FontNameDefault,
fontWeight: FontWeight.w300,
fontSize: MediumTextSize,
color: Colors.white,
);
const TitleTextStyle = TextStyle(
fontFamily: FontNameDefault,
fontWeight: FontWeight.w300,
fontSize: LargeTextSize,
color: TextColorDark,
);
const SubTitleTextStyle = TextStyle(
fontFamily: FontNameDefault,
fontWeight: FontWeight.w300,
fontSize: MediumTextSize,
color: TextColorAccent,
);
const CaptionTextStyle = TextStyle(
fontFamily: FontNameDefault,
fontWeight: FontWeight.w300,
fontSize: SmallTextSize,
color: TextColorDark,
);
const Body1TextStyle = TextStyle(
fontFamily: FontNameDefault,
fontWeight: FontWeight.w300,
fontSize: MediumTextSize,
color: Colors.black,
);
lib/app.dart
by adding:// lib/app.dart
// ...
ThemeData _theme() {
return ThemeData(
// ...
subtitle: SubTitleTextStyle,
caption: CaptionTextStyle,
// ...
}
We did a lot of coding in this lesson. Next, we'll learn some new concepts, namely how to test our app properly.