mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* Update project.pbxproj files to say Flutter rather than Chromium Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright. * Update the copyright notice checker to require a standard notice on all files * Update copyrights on Dart files. (This was a mechanical commit.) * Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine. Some were already marked "The Flutter Authors", not clear why. Their dates have been normalized. Some were missing the blank line after the license. Some were randomly different in trivial ways for no apparent reason (e.g. missing the trailing period). * Clean up the copyrights in non-Dart files. (Manual edits.) Also, make sure templates don't have copyrights. * Fix some more ORGANIZATIONNAMEs
410 lines
13 KiB
Dart
410 lines
13 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../gallery/demo.dart';
|
|
|
|
const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
|
|
|
|
enum CardDemoType {
|
|
standard,
|
|
tappable,
|
|
selectable,
|
|
}
|
|
|
|
class TravelDestination {
|
|
const TravelDestination({
|
|
@required this.assetName,
|
|
@required this.assetPackage,
|
|
@required this.title,
|
|
@required this.description,
|
|
@required this.city,
|
|
@required this.location,
|
|
this.type = CardDemoType.standard,
|
|
}) : assert(assetName != null),
|
|
assert(assetPackage != null),
|
|
assert(title != null),
|
|
assert(description != null),
|
|
assert(city != null),
|
|
assert(location != null);
|
|
|
|
final String assetName;
|
|
final String assetPackage;
|
|
final String title;
|
|
final String description;
|
|
final String city;
|
|
final String location;
|
|
final CardDemoType type;
|
|
}
|
|
|
|
const List<TravelDestination> destinations = <TravelDestination>[
|
|
TravelDestination(
|
|
assetName: 'places/india_thanjavur_market.png',
|
|
assetPackage: _kGalleryAssetsPackage,
|
|
title: 'Top 10 Cities to Visit in Tamil Nadu',
|
|
description: 'Number 10',
|
|
city: 'Thanjavur',
|
|
location: 'Thanjavur, Tamil Nadu',
|
|
),
|
|
TravelDestination(
|
|
assetName: 'places/india_chettinad_silk_maker.png',
|
|
assetPackage: _kGalleryAssetsPackage,
|
|
title: 'Artisans of Southern India',
|
|
description: 'Silk Spinners',
|
|
city: 'Chettinad',
|
|
location: 'Sivaganga, Tamil Nadu',
|
|
type: CardDemoType.tappable,
|
|
),
|
|
TravelDestination(
|
|
assetName: 'places/india_tanjore_thanjavur_temple.png',
|
|
assetPackage: _kGalleryAssetsPackage,
|
|
title: 'Brihadisvara Temple',
|
|
description: 'Temples',
|
|
city: 'Thanjavur',
|
|
location: 'Thanjavur, Tamil Nadu',
|
|
type: CardDemoType.selectable,
|
|
),
|
|
];
|
|
|
|
class TravelDestinationItem extends StatelessWidget {
|
|
const TravelDestinationItem({ Key key, @required this.destination, this.shape })
|
|
: assert(destination != null),
|
|
super(key: key);
|
|
|
|
// This height will allow for all the Card's content to fit comfortably within the card.
|
|
static const double height = 338.0;
|
|
final TravelDestination destination;
|
|
final ShapeBorder shape;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
children: <Widget>[
|
|
const SectionTitle(title: 'Normal'),
|
|
SizedBox(
|
|
height: height,
|
|
child: Card(
|
|
// This ensures that the Card's children are clipped correctly.
|
|
clipBehavior: Clip.antiAlias,
|
|
shape: shape,
|
|
child: TravelDestinationContent(destination: destination),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class TappableTravelDestinationItem extends StatelessWidget {
|
|
const TappableTravelDestinationItem({ Key key, @required this.destination, this.shape })
|
|
: assert(destination != null),
|
|
super(key: key);
|
|
|
|
// This height will allow for all the Card's content to fit comfortably within the card.
|
|
static const double height = 298.0;
|
|
final TravelDestination destination;
|
|
final ShapeBorder shape;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
children: <Widget>[
|
|
const SectionTitle(title: 'Tappable'),
|
|
SizedBox(
|
|
height: height,
|
|
child: Card(
|
|
// This ensures that the Card's children (including the ink splash) are clipped correctly.
|
|
clipBehavior: Clip.antiAlias,
|
|
shape: shape,
|
|
child: InkWell(
|
|
onTap: () {
|
|
print('Card was tapped');
|
|
},
|
|
// Generally, material cards use onSurface with 12% opacity for the pressed state.
|
|
splashColor: Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
|
|
// Generally, material cards do not have a highlight overlay.
|
|
highlightColor: Colors.transparent,
|
|
child: TravelDestinationContent(destination: destination),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SelectableTravelDestinationItem extends StatefulWidget {
|
|
const SelectableTravelDestinationItem({ Key key, @required this.destination, this.shape })
|
|
: assert(destination != null),
|
|
super(key: key);
|
|
|
|
final TravelDestination destination;
|
|
final ShapeBorder shape;
|
|
|
|
@override
|
|
_SelectableTravelDestinationItemState createState() => _SelectableTravelDestinationItemState();
|
|
}
|
|
|
|
class _SelectableTravelDestinationItemState extends State<SelectableTravelDestinationItem> {
|
|
|
|
// This height will allow for all the Card's content to fit comfortably within the card.
|
|
static const double height = 298.0;
|
|
bool _isSelected = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Column(
|
|
children: <Widget>[
|
|
const SectionTitle(title: 'Selectable (long press)'),
|
|
SizedBox(
|
|
height: height,
|
|
child: Card(
|
|
// This ensures that the Card's children (including the ink splash) are clipped correctly.
|
|
clipBehavior: Clip.antiAlias,
|
|
shape: widget.shape,
|
|
child: InkWell(
|
|
onLongPress: () {
|
|
print('Selectable card state changed');
|
|
setState(() {
|
|
_isSelected = !_isSelected;
|
|
});
|
|
},
|
|
// Generally, material cards use onSurface with 12% opacity for the pressed state.
|
|
splashColor: colorScheme.onSurface.withOpacity(0.12),
|
|
// Generally, material cards do not have a highlight overlay.
|
|
highlightColor: Colors.transparent,
|
|
child: Stack(
|
|
children: <Widget>[
|
|
Container(
|
|
color: _isSelected
|
|
// Generally, material cards use primary with 8% opacity for the selected state.
|
|
// See: https://material.io/design/interaction/states.html#anatomy
|
|
? colorScheme.primary.withOpacity(0.08)
|
|
: Colors.transparent,
|
|
),
|
|
TravelDestinationContent(destination: widget.destination),
|
|
Align(
|
|
alignment: Alignment.topRight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Icon(
|
|
Icons.check_circle,
|
|
color: _isSelected ? colorScheme.primary : Colors.transparent,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SectionTitle extends StatelessWidget {
|
|
const SectionTitle({
|
|
Key key,
|
|
this.title,
|
|
}) : super(key: key);
|
|
|
|
final String title;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Padding(
|
|
padding: const EdgeInsets.fromLTRB(4.0, 4.0, 4.0, 12.0),
|
|
child: Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(title, style: Theme.of(context).textTheme.subhead),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class TravelDestinationContent extends StatelessWidget {
|
|
const TravelDestinationContent({ Key key, @required this.destination })
|
|
: assert(destination != null),
|
|
super(key: key);
|
|
|
|
final TravelDestination destination;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ThemeData theme = Theme.of(context);
|
|
final TextStyle titleStyle = theme.textTheme.headline.copyWith(color: Colors.white);
|
|
final TextStyle descriptionStyle = theme.textTheme.subhead;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
// Photo and title.
|
|
SizedBox(
|
|
height: 184.0,
|
|
child: Stack(
|
|
children: <Widget>[
|
|
Positioned.fill(
|
|
// In order to have the ink splash appear above the image, you
|
|
// must use Ink.image. This allows the image to be painted as part
|
|
// of the Material and display ink effects above it. Using a
|
|
// standard Image will obscure the ink splash.
|
|
child: Ink.image(
|
|
image: AssetImage(destination.assetName, package: destination.assetPackage),
|
|
fit: BoxFit.cover,
|
|
child: Container(),
|
|
),
|
|
),
|
|
Positioned(
|
|
bottom: 16.0,
|
|
left: 16.0,
|
|
right: 16.0,
|
|
child: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
destination.title,
|
|
style: titleStyle,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
// Description and share/explore buttons.
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
|
|
child: DefaultTextStyle(
|
|
softWrap: false,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: descriptionStyle,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
// three line description
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 8.0),
|
|
child: Text(
|
|
destination.description,
|
|
style: descriptionStyle.copyWith(color: Colors.black54),
|
|
),
|
|
),
|
|
Text(destination.city),
|
|
Text(destination.location),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
if (destination.type == CardDemoType.standard)
|
|
// share, explore buttons
|
|
ButtonBar(
|
|
alignment: MainAxisAlignment.start,
|
|
children: <Widget>[
|
|
FlatButton(
|
|
child: Text('SHARE', semanticsLabel: 'Share ${destination.title}'),
|
|
textColor: Colors.amber.shade500,
|
|
onPressed: () { print('pressed'); },
|
|
),
|
|
FlatButton(
|
|
child: Text('EXPLORE', semanticsLabel: 'Explore ${destination.title}'),
|
|
textColor: Colors.amber.shade500,
|
|
onPressed: () { print('pressed'); },
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class CardsDemo extends StatefulWidget {
|
|
static const String routeName = '/material/cards';
|
|
|
|
@override
|
|
_CardsDemoState createState() => _CardsDemoState();
|
|
}
|
|
|
|
class _CardsDemoState extends State<CardsDemo> {
|
|
ShapeBorder _shape;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Cards'),
|
|
actions: <Widget>[
|
|
MaterialDemoDocumentationButton(CardsDemo.routeName),
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.sentiment_very_satisfied,
|
|
semanticLabel: 'update shape',
|
|
),
|
|
onPressed: () {
|
|
setState(() {
|
|
_shape = _shape != null ? null : const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(16.0),
|
|
topRight: Radius.circular(16.0),
|
|
bottomLeft: Radius.circular(2.0),
|
|
bottomRight: Radius.circular(2.0),
|
|
),
|
|
);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: Scrollbar(
|
|
child: ListView(
|
|
padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0),
|
|
children: destinations.map<Widget>((TravelDestination destination) {
|
|
Widget child;
|
|
switch (destination.type) {
|
|
case CardDemoType.standard:
|
|
child = TravelDestinationItem(destination: destination, shape: _shape);
|
|
break;
|
|
case CardDemoType.tappable:
|
|
child = TappableTravelDestinationItem(destination: destination, shape: _shape);
|
|
break;
|
|
case CardDemoType.selectable:
|
|
child = SelectableTravelDestinationItem(destination: destination, shape: _shape);
|
|
break;
|
|
}
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 8.0),
|
|
child: child,
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|