1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-11 07:18:15 +00:00

[Gallery] Fix directory structure (#312)

This commit is contained in:
Pierre-Louis
2020-02-05 20:11:54 +01:00
committed by GitHub
parent 082592e9a9
commit cee267cf88
762 changed files with 12 additions and 12 deletions

View File

@@ -0,0 +1,125 @@
// Copyright 2020 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN bannerDemo
enum BannerDemoAction {
reset,
showMultipleActions,
showLeading,
}
class BannerDemo extends StatefulWidget {
@override
_BannerDemoState createState() => _BannerDemoState();
}
class _BannerDemoState extends State<BannerDemo> {
static const _itemCount = 20;
var _displayBanner = true;
var _showMultipleActions = true;
var _showLeading = true;
void handleDemoAction(BannerDemoAction action) {
setState(() {
switch (action) {
case BannerDemoAction.reset:
_displayBanner = true;
_showMultipleActions = true;
_showLeading = true;
break;
case BannerDemoAction.showMultipleActions:
_showMultipleActions = !_showMultipleActions;
break;
case BannerDemoAction.showLeading:
_showLeading = !_showLeading;
break;
}
});
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final banner = MaterialBanner(
content: Text(GalleryLocalizations.of(context).bannerDemoText),
leading: _showLeading
? CircleAvatar(
child: Icon(Icons.access_alarm, color: colorScheme.onPrimary),
backgroundColor: colorScheme.primary,
)
: null,
actions: [
FlatButton(
child: Text(GalleryLocalizations.of(context).signIn),
onPressed: () {
setState(() {
_displayBanner = false;
});
},
),
if (_showMultipleActions)
FlatButton(
child: Text(GalleryLocalizations.of(context).dismiss),
onPressed: () {
setState(() {
_displayBanner = false;
});
},
),
],
backgroundColor: colorScheme.background,
);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoBannerTitle),
actions: [
PopupMenuButton<BannerDemoAction>(
onSelected: handleDemoAction,
itemBuilder: (context) => <PopupMenuEntry<BannerDemoAction>>[
PopupMenuItem<BannerDemoAction>(
value: BannerDemoAction.reset,
child:
Text(GalleryLocalizations.of(context).bannerDemoResetText),
),
const PopupMenuDivider(),
CheckedPopupMenuItem<BannerDemoAction>(
value: BannerDemoAction.showMultipleActions,
checked: _showMultipleActions,
child: Text(
GalleryLocalizations.of(context).bannerDemoMultipleText),
),
CheckedPopupMenuItem<BannerDemoAction>(
value: BannerDemoAction.showLeading,
checked: _showLeading,
child: Text(
GalleryLocalizations.of(context).bannerDemoLeadingText),
),
],
),
],
),
body: ListView.builder(
itemCount: _displayBanner ? _itemCount + 1 : _itemCount,
itemBuilder: (context, index) {
if (index == 0 && _displayBanner) {
return banner;
}
return ListTile(
title: Text(
GalleryLocalizations.of(context)
.starterAppDrawerItem(_displayBanner ? index : index + 1),
),
);
}),
);
}
}
// END

View File

@@ -0,0 +1,169 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN bottomAppBarDemo
class BottomAppBarDemo extends StatefulWidget {
@override
State createState() => _BottomAppBarDemoState();
}
class _BottomAppBarDemoState extends State<BottomAppBarDemo> {
var _showFab = true;
var _showNotch = true;
var _fabLocation = FloatingActionButtonLocation.endDocked;
void _onShowNotchChanged(bool value) {
setState(() {
_showNotch = value;
});
}
void _onShowFabChanged(bool value) {
setState(() {
_showFab = value;
});
}
void _onFabLocationChanged(FloatingActionButtonLocation value) {
setState(() {
_fabLocation = value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoBottomAppBarTitle),
),
body: ListView(
padding: const EdgeInsets.only(bottom: 88),
children: [
SwitchListTile(
title: Text(
GalleryLocalizations.of(context).demoFloatingButtonTitle,
),
value: _showFab,
onChanged: _onShowFabChanged,
),
SwitchListTile(
title: Text(GalleryLocalizations.of(context).bottomAppBarNotch),
value: _showNotch,
onChanged: _onShowNotchChanged,
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(GalleryLocalizations.of(context).bottomAppBarPosition),
),
RadioListTile<FloatingActionButtonLocation>(
title: Text(
GalleryLocalizations.of(context).bottomAppBarPositionDockedEnd,
),
value: FloatingActionButtonLocation.endDocked,
groupValue: _fabLocation,
onChanged: _onFabLocationChanged,
),
RadioListTile<FloatingActionButtonLocation>(
title: Text(
GalleryLocalizations.of(context).bottomAppBarPositionDockedCenter,
),
value: FloatingActionButtonLocation.centerDocked,
groupValue: _fabLocation,
onChanged: _onFabLocationChanged,
),
RadioListTile<FloatingActionButtonLocation>(
title: Text(
GalleryLocalizations.of(context).bottomAppBarPositionFloatingEnd,
),
value: FloatingActionButtonLocation.endFloat,
groupValue: _fabLocation,
onChanged: _onFabLocationChanged,
),
RadioListTile<FloatingActionButtonLocation>(
title: Text(
GalleryLocalizations.of(context)
.bottomAppBarPositionFloatingCenter,
),
value: FloatingActionButtonLocation.centerFloat,
groupValue: _fabLocation,
onChanged: _onFabLocationChanged,
),
],
),
floatingActionButton: _showFab
? FloatingActionButton(
onPressed: () {
print('Floating action button pressed');
},
child: Icon(Icons.add),
tooltip: GalleryLocalizations.of(context).buttonTextCreate,
)
: null,
floatingActionButtonLocation: _fabLocation,
bottomNavigationBar: _DemoBottomAppBar(
fabLocation: _fabLocation,
shape: _showNotch ? CircularNotchedRectangle() : null,
),
);
}
}
class _DemoBottomAppBar extends StatelessWidget {
const _DemoBottomAppBar({
this.fabLocation,
this.shape,
});
final FloatingActionButtonLocation fabLocation;
final NotchedShape shape;
static final centerLocations = <FloatingActionButtonLocation>[
FloatingActionButtonLocation.centerDocked,
FloatingActionButtonLocation.centerFloat,
];
@override
Widget build(BuildContext context) {
return BottomAppBar(
shape: shape,
child: IconTheme(
data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary),
child: Row(
children: [
IconButton(
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
icon: const Icon(Icons.menu),
onPressed: () {
print('Menu button pressed');
},
),
if (centerLocations.contains(fabLocation)) const Spacer(),
IconButton(
tooltip: GalleryLocalizations.of(context).starterAppTooltipSearch,
icon: const Icon(Icons.search),
onPressed: () {
print('Search button pressed');
},
),
IconButton(
tooltip:
GalleryLocalizations.of(context).starterAppTooltipFavorite,
icon: const Icon(Icons.favorite),
onPressed: () {
print('Favorite button pressed');
},
),
],
),
),
);
}
}
// END

View File

@@ -0,0 +1,212 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN bottomNavigationDemo
enum BottomNavigationDemoType {
withLabels,
withoutLabels,
}
class BottomNavigationDemo extends StatefulWidget {
BottomNavigationDemo({Key key, @required this.type}) : super(key: key);
final BottomNavigationDemoType type;
@override
_BottomNavigationDemoState createState() => _BottomNavigationDemoState();
}
class _BottomNavigationDemoState extends State<BottomNavigationDemo>
with TickerProviderStateMixin {
int _currentIndex = 0;
List<_NavigationIconView> _navigationViews;
String _title(BuildContext context) {
switch (widget.type) {
case BottomNavigationDemoType.withLabels:
return GalleryLocalizations.of(context)
.demoBottomNavigationPersistentLabels;
case BottomNavigationDemoType.withoutLabels:
return GalleryLocalizations.of(context)
.demoBottomNavigationSelectedLabel;
}
return '';
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_navigationViews == null) {
_navigationViews = <_NavigationIconView>[
_NavigationIconView(
icon: const Icon(Icons.add_comment),
title: GalleryLocalizations.of(context).bottomNavigationCommentsTab,
vsync: this,
),
_NavigationIconView(
icon: const Icon(Icons.calendar_today),
title: GalleryLocalizations.of(context).bottomNavigationCalendarTab,
vsync: this,
),
_NavigationIconView(
icon: const Icon(Icons.account_circle),
title: GalleryLocalizations.of(context).bottomNavigationAccountTab,
vsync: this,
),
_NavigationIconView(
icon: const Icon(Icons.alarm_on),
title: GalleryLocalizations.of(context).bottomNavigationAlarmTab,
vsync: this,
),
_NavigationIconView(
icon: const Icon(Icons.camera_enhance),
title: GalleryLocalizations.of(context).bottomNavigationCameraTab,
vsync: this,
),
];
_navigationViews[_currentIndex].controller.value = 1;
}
}
@override
void dispose() {
for (_NavigationIconView view in _navigationViews) {
view.controller.dispose();
}
super.dispose();
}
Widget _buildTransitionsStack() {
final List<FadeTransition> transitions = <FadeTransition>[];
for (_NavigationIconView view in _navigationViews) {
transitions.add(view.transition(context));
}
// We want to have the newly animating (fading in) views on top.
transitions.sort((a, b) {
final aAnimation = a.opacity;
final bAnimation = b.opacity;
final aValue = aAnimation.value;
final bValue = bAnimation.value;
return aValue.compareTo(bValue);
});
return Stack(children: transitions);
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
var bottomNavigationBarItems = _navigationViews
.map<BottomNavigationBarItem>((navigationView) => navigationView.item)
.toList();
if (widget.type == BottomNavigationDemoType.withLabels) {
bottomNavigationBarItems =
bottomNavigationBarItems.sublist(0, _navigationViews.length - 2);
_currentIndex =
_currentIndex.clamp(0, bottomNavigationBarItems.length - 1).toInt();
}
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: Center(
child: _buildTransitionsStack(),
),
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels:
widget.type == BottomNavigationDemoType.withLabels,
items: bottomNavigationBarItems,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedFontSize: textTheme.caption.fontSize,
unselectedFontSize: textTheme.caption.fontSize,
onTap: (index) {
setState(() {
_navigationViews[_currentIndex].controller.reverse();
_currentIndex = index;
_navigationViews[_currentIndex].controller.forward();
});
},
selectedItemColor: colorScheme.onPrimary,
unselectedItemColor: colorScheme.onPrimary.withOpacity(0.38),
backgroundColor: colorScheme.primary,
),
);
}
}
class _NavigationIconView {
_NavigationIconView({
this.title,
this.icon,
TickerProvider vsync,
}) : item = BottomNavigationBarItem(
icon: icon,
title: Text(title),
),
controller = AnimationController(
duration: kThemeAnimationDuration,
vsync: vsync,
) {
_animation = controller.drive(CurveTween(
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
));
}
final String title;
final Widget icon;
final BottomNavigationBarItem item;
final AnimationController controller;
Animation<double> _animation;
FadeTransition transition(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: Stack(
children: [
ExcludeSemantics(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(
'assets/demos/bottom_navigation_background.png',
),
),
),
),
),
Center(
child: IconTheme(
data: IconThemeData(
color: Colors.white,
size: 80,
),
child: Semantics(
label: GalleryLocalizations.of(context)
.bottomNavigationContentPlaceholder(title),
child: icon,
),
),
),
],
),
);
}
}
// END

View File

@@ -0,0 +1,192 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum BottomSheetDemoType {
persistent,
modal,
}
class BottomSheetDemo extends StatelessWidget {
BottomSheetDemo({Key key, @required this.type}) : super(key: key);
final BottomSheetDemoType type;
String _title(BuildContext context) {
switch (type) {
case BottomSheetDemoType.persistent:
return GalleryLocalizations.of(context).demoBottomSheetPersistentTitle;
case BottomSheetDemoType.modal:
return GalleryLocalizations.of(context).demoBottomSheetModalTitle;
}
return '';
}
Widget _bottomSheetDemo(BuildContext context) {
switch (type) {
case BottomSheetDemoType.persistent:
return _PersistentBottomSheetDemo();
break;
case BottomSheetDemoType.modal:
default:
return _ModalBottomSheetDemo();
break;
}
}
@override
Widget build(BuildContext context) {
// We wrap the demo in a [Navigator] to make sure that the modal bottom
// sheets gets dismissed when changing demo.
return Navigator(
// Adding [ValueKey] to make sure that the widget gets rebuilt when
// changing type.
key: ValueKey(type),
onGenerateRoute: (settings) {
return MaterialPageRoute<Widget>(
builder: (context) => Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Theme.of(context).colorScheme.secondary,
child: Icon(
Icons.add,
semanticLabel:
GalleryLocalizations.of(context).demoBottomSheetAddLabel,
),
),
body: _bottomSheetDemo(context),
),
settings: settings,
);
},
);
}
}
// BEGIN bottomSheetDemoModal#1 bottomSheetDemoPersistent#1
class _BottomSheetContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 300,
child: Column(
children: [
Container(
height: 70,
child: Center(
child: Text(
GalleryLocalizations.of(context).demoBottomSheetHeader,
textAlign: TextAlign.center,
),
),
),
Divider(thickness: 1),
Expanded(
child: ListView.builder(
itemCount: 21,
itemBuilder: (context, index) {
return ListTile(
title: Text(GalleryLocalizations.of(context)
.demoBottomSheetItem(index)),
);
},
),
),
],
),
);
}
}
// END bottomSheetDemoModal#1 bottomSheetDemoPersistent#1
// BEGIN bottomSheetDemoModal#2
class _ModalBottomSheetDemo extends StatelessWidget {
void _showModalBottomSheet(BuildContext context) {
showModalBottomSheet<void>(
context: context,
builder: (context) {
return _BottomSheetContent();
},
);
}
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: () {
_showModalBottomSheet(context);
},
child: Text(GalleryLocalizations.of(context).demoBottomSheetButtonText),
),
);
}
}
// END
// BEGIN bottomSheetDemoPersistent#2
class _PersistentBottomSheetDemo extends StatefulWidget {
@override
_PersistentBottomSheetDemoState createState() =>
_PersistentBottomSheetDemoState();
}
class _PersistentBottomSheetDemoState
extends State<_PersistentBottomSheetDemo> {
VoidCallback _showBottomSheetCallback;
@override
void initState() {
super.initState();
_showBottomSheetCallback = _showPersistentBottomSheet;
}
void _showPersistentBottomSheet() {
setState(() {
// Disable the show bottom sheet button.
_showBottomSheetCallback = null;
});
Scaffold.of(context)
.showBottomSheet<void>(
(context) {
return _BottomSheetContent();
},
elevation: 25,
)
.closed
.whenComplete(() {
if (mounted) {
setState(() {
// Re-enable the bottom sheet button.
_showBottomSheetCallback = _showPersistentBottomSheet;
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: _showBottomSheetCallback,
child: Text(GalleryLocalizations.of(context).demoBottomSheetButtonText),
),
);
}
}
// END

View File

@@ -0,0 +1,214 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum ButtonDemoType {
flat,
raised,
outline,
toggle,
floating,
}
class ButtonDemo extends StatelessWidget {
const ButtonDemo({Key key, this.type}) : super(key: key);
final ButtonDemoType type;
String _title(BuildContext context) {
switch (type) {
case ButtonDemoType.flat:
return GalleryLocalizations.of(context).demoFlatButtonTitle;
case ButtonDemoType.raised:
return GalleryLocalizations.of(context).demoRaisedButtonTitle;
case ButtonDemoType.outline:
return GalleryLocalizations.of(context).demoOutlineButtonTitle;
case ButtonDemoType.toggle:
return GalleryLocalizations.of(context).demoToggleButtonTitle;
case ButtonDemoType.floating:
return GalleryLocalizations.of(context).demoFloatingButtonTitle;
}
return '';
}
@override
Widget build(BuildContext context) {
Widget buttons;
switch (type) {
case ButtonDemoType.flat:
buttons = _FlatButtonDemo();
break;
case ButtonDemoType.raised:
buttons = _RaisedButtonDemo();
break;
case ButtonDemoType.outline:
buttons = _OutlineButtonDemo();
break;
case ButtonDemoType.toggle:
buttons = _ToggleButtonsDemo();
break;
case ButtonDemoType.floating:
buttons = _FloatingActionButtonDemo();
break;
}
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: buttons,
);
}
}
// BEGIN buttonDemoFlat
class _FlatButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FlatButton(
child: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
SizedBox(height: 12),
FlatButton.icon(
icon: const Icon(Icons.add, size: 18),
label: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
],
),
);
}
}
// END
// BEGIN buttonDemoRaised
class _RaisedButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
RaisedButton(
child: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
SizedBox(height: 12),
RaisedButton.icon(
icon: const Icon(Icons.add, size: 18),
label: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
],
),
);
}
}
// END
// BEGIN buttonDemoOutline
class _OutlineButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
OutlineButton(
// TODO: Should update to OutlineButton follow material spec.
highlightedBorderColor:
Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
child: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
SizedBox(height: 12),
OutlineButton.icon(
// TODO: Should update to OutlineButton follow material spec.
highlightedBorderColor:
Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
icon: const Icon(Icons.add, size: 18),
label: Text(GalleryLocalizations.of(context).buttonText),
onPressed: () {},
),
],
),
);
}
}
// END
// BEGIN buttonDemoToggle
class _ToggleButtonsDemo extends StatefulWidget {
@override
_ToggleButtonsDemoState createState() => _ToggleButtonsDemoState();
}
class _ToggleButtonsDemoState extends State<_ToggleButtonsDemo> {
final isSelected = <bool>[false, false, false];
@override
Widget build(BuildContext context) {
return Center(
child: ToggleButtons(
children: [
Icon(Icons.ac_unit),
Icon(Icons.call),
Icon(Icons.cake),
],
onPressed: (index) {
setState(() {
isSelected[index] = !isSelected[index];
});
},
isSelected: isSelected,
),
);
}
}
// END
// BEGIN buttonDemoFloating
class _FloatingActionButtonDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {},
tooltip: GalleryLocalizations.of(context).buttonTextCreate,
),
SizedBox(height: 20),
FloatingActionButton.extended(
icon: const Icon(Icons.add),
label: Text(GalleryLocalizations.of(context).buttonTextCreate),
onPressed: () {},
),
],
),
);
}
}
// END

View File

@@ -0,0 +1,411 @@
// Copyright 2020 The Flutter team. 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 'package:gallery/l10n/gallery_localizations.dart';
const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
// BEGIN cardsDemo
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;
}
List<TravelDestination> destinations(BuildContext context) => [
TravelDestination(
assetName: 'places/india_thanjavur_market.png',
assetPackage: _kGalleryAssetsPackage,
title:
GalleryLocalizations.of(context).cardsDemoTravelDestinationTitle1,
description: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationDescription1,
city: GalleryLocalizations.of(context).cardsDemoTravelDestinationCity1,
location: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationLocation1,
),
TravelDestination(
assetName: 'places/india_chettinad_silk_maker.png',
assetPackage: _kGalleryAssetsPackage,
title:
GalleryLocalizations.of(context).cardsDemoTravelDestinationTitle2,
description: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationDescription2,
city: GalleryLocalizations.of(context).cardsDemoTravelDestinationCity2,
location: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationLocation2,
type: CardDemoType.tappable,
),
TravelDestination(
assetName: 'places/india_tanjore_thanjavur_temple.png',
assetPackage: _kGalleryAssetsPackage,
title:
GalleryLocalizations.of(context).cardsDemoTravelDestinationTitle3,
description: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationDescription3,
city: GalleryLocalizations.of(context).cardsDemoTravelDestinationCity1,
location: GalleryLocalizations.of(context)
.cardsDemoTravelDestinationLocation1,
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 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),
child: Column(
children: [
SectionTitle(
title:
GalleryLocalizations.of(context).settingsTextScalingNormal),
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 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),
child: Column(
children: [
SectionTitle(
title: GalleryLocalizations.of(context).cardsDemoTappable),
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 height = 298.0;
var _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),
child: Column(
children: [
SectionTitle(
title: GalleryLocalizations.of(context).cardsDemoSelectable),
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: [
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),
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, 4, 4, 12),
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: [
SizedBox(
height: 184,
child: Stack(
children: [
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,
left: 16,
right: 16,
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, 16, 16, 0),
child: DefaultTextStyle(
softWrap: false,
overflow: TextOverflow.ellipsis,
style: descriptionStyle,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// This array contains the three line description on each card
// demo.
Padding(
padding: const EdgeInsets.only(bottom: 8),
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: [
FlatButton(
child: Text(GalleryLocalizations.of(context).demoMenuShare,
semanticsLabel: GalleryLocalizations.of(context)
.cardsDemoShareSemantics(destination.title)),
textColor: Colors.amber.shade500,
onPressed: () {
print('pressed');
},
),
FlatButton(
child: Text(GalleryLocalizations.of(context).cardsDemoExplore,
semanticsLabel: GalleryLocalizations.of(context)
.cardsDemoExploreSemantics(destination.title)),
textColor: Colors.amber.shade500,
onPressed: () {
print('pressed');
},
),
],
),
],
);
}
}
class CardsDemo extends StatefulWidget {
@override
_CardsDemoState createState() => _CardsDemoState();
}
class _CardsDemoState extends State<CardsDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoCardTitle),
),
body: Scrollbar(
child: ListView(
padding: const EdgeInsets.only(top: 8, left: 8, right: 8),
children: [
for (final destination in destinations(context))
Container(
margin: const EdgeInsets.only(bottom: 8),
child: (destination.type == CardDemoType.standard)
? TravelDestinationItem(destination: destination)
: destination.type == CardDemoType.tappable
? TappableTravelDestinationItem(
destination: destination)
: SelectableTravelDestinationItem(
destination: destination),
),
],
),
),
);
}
}
// END

View File

@@ -0,0 +1,215 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum ChipDemoType {
action,
choice,
filter,
input,
}
class ChipDemo extends StatelessWidget {
const ChipDemo({Key key, this.type}) : super(key: key);
final ChipDemoType type;
String _title(BuildContext context) {
switch (type) {
case ChipDemoType.action:
return GalleryLocalizations.of(context).demoActionChipTitle;
case ChipDemoType.choice:
return GalleryLocalizations.of(context).demoChoiceChipTitle;
case ChipDemoType.filter:
return GalleryLocalizations.of(context).demoFilterChipTitle;
case ChipDemoType.input:
return GalleryLocalizations.of(context).demoInputChipTitle;
}
return '';
}
@override
Widget build(BuildContext context) {
Widget buttons;
switch (type) {
case ChipDemoType.action:
buttons = _ActionChipDemo();
break;
case ChipDemoType.choice:
buttons = _ChoiceChipDemo();
break;
case ChipDemoType.filter:
buttons = _FilterChipDemo();
break;
case ChipDemoType.input:
buttons = _InputChipDemo();
break;
}
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: buttons,
);
}
}
// BEGIN chipDemoAction
class _ActionChipDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ActionChip(
onPressed: () {},
avatar: Icon(
Icons.brightness_5,
color: Colors.black54,
),
label: Text(GalleryLocalizations.of(context).chipTurnOnLights),
),
);
}
}
// END
// BEGIN chipDemoChoice
class _ChoiceChipDemo extends StatefulWidget {
@override
_ChoiceChipDemoState createState() => _ChoiceChipDemoState();
}
class _ChoiceChipDemoState extends State<_ChoiceChipDemo> {
int indexSelected = -1;
@override
Widget build(BuildContext context) {
return Center(
child: Wrap(
children: [
ChoiceChip(
label: Text(GalleryLocalizations.of(context).chipSmall),
selected: indexSelected == 0,
onSelected: (value) {
setState(() {
indexSelected = value ? 0 : -1;
});
},
),
SizedBox(width: 8),
ChoiceChip(
label: Text(GalleryLocalizations.of(context).chipMedium),
selected: indexSelected == 1,
onSelected: (value) {
setState(() {
indexSelected = value ? 1 : -1;
});
},
),
SizedBox(width: 8),
ChoiceChip(
label: Text(GalleryLocalizations.of(context).chipLarge),
selected: indexSelected == 2,
onSelected: (value) {
setState(() {
indexSelected = value ? 2 : -1;
});
},
),
],
),
);
}
}
// END
// BEGIN chipDemoFilter
class _FilterChipDemo extends StatefulWidget {
@override
_FilterChipDemoState createState() => _FilterChipDemoState();
}
class _FilterChipDemoState extends State<_FilterChipDemo> {
bool isSelectedElevator = false;
bool isSelectedWasher = false;
bool isSelectedFireplace = false;
@override
Widget build(BuildContext context) {
final chips = [
FilterChip(
label: Text(GalleryLocalizations.of(context).chipElevator),
selected: isSelectedElevator,
onSelected: (value) {
setState(() {
isSelectedElevator = !isSelectedElevator;
});
},
),
FilterChip(
label: Text(GalleryLocalizations.of(context).chipWasher),
selected: isSelectedWasher,
onSelected: (value) {
setState(() {
isSelectedWasher = !isSelectedWasher;
});
},
),
FilterChip(
label: Text(GalleryLocalizations.of(context).chipFireplace),
selected: isSelectedFireplace,
onSelected: (value) {
setState(() {
isSelectedFireplace = !isSelectedFireplace;
});
},
),
];
return Center(
child: Wrap(
children: [
for (final chip in chips)
Padding(
padding: const EdgeInsets.all(4),
child: chip,
)
],
),
);
}
}
// END
// BEGIN chipDemoInput
class _InputChipDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: InputChip(
onPressed: () {},
onDeleted: () {},
avatar: Icon(
Icons.directions_bike,
size: 20,
color: Colors.black54,
),
deleteIconColor: Colors.black54,
label: Text(GalleryLocalizations.of(context).chipBiking),
),
);
}
}
// END

View File

@@ -0,0 +1,551 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:gallery/data/gallery_options.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:intl/intl.dart';
// BEGIN dataTableDemo
class DataTableDemo extends StatefulWidget {
@override
_DataTableDemoState createState() => _DataTableDemoState();
}
class _DataTableDemoState extends State<DataTableDemo> {
int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
int _sortColumnIndex;
bool _sortAscending = true;
_DessertDataSource _dessertsDataSource;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_dessertsDataSource == null) {
_dessertsDataSource = _DessertDataSource(context);
}
}
void _sort<T>(
Comparable<T> getField(_Dessert d), int columnIndex, bool ascending) {
_dessertsDataSource._sort<T>(getField, ascending);
setState(() {
_sortColumnIndex = columnIndex;
_sortAscending = ascending;
});
}
@override
Widget build(BuildContext context) {
final localizations = GalleryLocalizations.of(context);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(localizations.demoDataTableTitle),
),
body: Scrollbar(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
PaginatedDataTable(
header: Text(localizations.dataTableHeader),
rowsPerPage: _rowsPerPage,
onRowsPerPageChanged: (value) {
setState(() {
_rowsPerPage = value;
});
},
sortColumnIndex: _sortColumnIndex,
sortAscending: _sortAscending,
onSelectAll: _dessertsDataSource._selectAll,
columns: [
DataColumn(
label: Text(localizations.dataTableColumnDessert),
onSort: (columnIndex, ascending) =>
_sort<String>((d) => d.name, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnCalories),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calories, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnFat),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.fat, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnCarbs),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.carbs, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnProtein),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.protein, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnSodium),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.sodium, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnCalcium),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.calcium, columnIndex, ascending),
),
DataColumn(
label: Text(localizations.dataTableColumnIron),
numeric: true,
onSort: (columnIndex, ascending) =>
_sort<num>((d) => d.iron, columnIndex, ascending),
),
],
source: _dessertsDataSource,
),
],
),
),
);
}
}
class _Dessert {
_Dessert(this.name, this.calories, this.fat, this.carbs, this.protein,
this.sodium, this.calcium, this.iron);
final String name;
final int calories;
final double fat;
final int carbs;
final double protein;
final int sodium;
final int calcium;
final int iron;
bool selected = false;
}
class _DessertDataSource extends DataTableSource {
_DessertDataSource(this.context) {
final localizations = GalleryLocalizations.of(context);
_desserts = <_Dessert>[
_Dessert(
localizations.dataTableRowFrozenYogurt,
159,
6.0,
24,
4.0,
87,
14,
1,
),
_Dessert(
localizations.dataTableRowIceCreamSandwich,
237,
9.0,
37,
4.3,
129,
8,
1,
),
_Dessert(
localizations.dataTableRowEclair,
262,
16.0,
24,
6.0,
337,
6,
7,
),
_Dessert(
localizations.dataTableRowCupcake,
305,
3.7,
67,
4.3,
413,
3,
8,
),
_Dessert(
localizations.dataTableRowGingerbread,
356,
16.0,
49,
3.9,
327,
7,
16,
),
_Dessert(
localizations.dataTableRowJellyBean,
375,
0.0,
94,
0.0,
50,
0,
0,
),
_Dessert(
localizations.dataTableRowLollipop,
392,
0.2,
98,
0.0,
38,
0,
2,
),
_Dessert(
localizations.dataTableRowHoneycomb,
408,
3.2,
87,
6.5,
562,
0,
45,
),
_Dessert(
localizations.dataTableRowDonut,
452,
25.0,
51,
4.9,
326,
2,
22,
),
_Dessert(
localizations.dataTableRowApplePie,
518,
26.0,
65,
7.0,
54,
12,
6,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowFrozenYogurt,
),
168,
6.0,
26,
4.0,
87,
14,
1,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowIceCreamSandwich,
),
246,
9.0,
39,
4.3,
129,
8,
1,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowEclair,
),
271,
16.0,
26,
6.0,
337,
6,
7,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowCupcake,
),
314,
3.7,
69,
4.3,
413,
3,
8,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowGingerbread,
),
345,
16.0,
51,
3.9,
327,
7,
16,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowJellyBean,
),
364,
0.0,
96,
0.0,
50,
0,
0,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowLollipop,
),
401,
0.2,
100,
0.0,
38,
0,
2,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowHoneycomb,
),
417,
3.2,
89,
6.5,
562,
0,
45,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowDonut,
),
461,
25.0,
53,
4.9,
326,
2,
22,
),
_Dessert(
localizations.dataTableRowWithSugar(
localizations.dataTableRowApplePie,
),
527,
26.0,
67,
7.0,
54,
12,
6,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowFrozenYogurt,
),
223,
6.0,
36,
4.0,
87,
14,
1,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowIceCreamSandwich,
),
301,
9.0,
49,
4.3,
129,
8,
1,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowEclair,
),
326,
16.0,
36,
6.0,
337,
6,
7,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowCupcake,
),
369,
3.7,
79,
4.3,
413,
3,
8,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowGingerbread,
),
420,
16.0,
61,
3.9,
327,
7,
16,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowJellyBean,
),
439,
0.0,
106,
0.0,
50,
0,
0,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowLollipop,
),
456,
0.2,
110,
0.0,
38,
0,
2,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowHoneycomb,
),
472,
3.2,
99,
6.5,
562,
0,
45,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowDonut,
),
516,
25.0,
63,
4.9,
326,
2,
22,
),
_Dessert(
localizations.dataTableRowWithHoney(
localizations.dataTableRowApplePie,
),
582,
26.0,
77,
7.0,
54,
12,
6,
),
];
}
final BuildContext context;
List<_Dessert> _desserts;
void _sort<T>(Comparable<T> getField(_Dessert d), bool ascending) {
_desserts.sort((a, b) {
final Comparable<T> aValue = getField(a);
final Comparable<T> bValue = getField(b);
return ascending
? Comparable.compare(aValue, bValue)
: Comparable.compare(bValue, aValue);
});
notifyListeners();
}
int _selectedCount = 0;
@override
DataRow getRow(int index) {
final format = NumberFormat.decimalPercentPattern(
locale: GalleryOptions.of(context).locale.toString(),
decimalDigits: 0,
);
assert(index >= 0);
if (index >= _desserts.length) return null;
final _Dessert dessert = _desserts[index];
return DataRow.byIndex(
index: index,
selected: dessert.selected,
onSelectChanged: (value) {
if (dessert.selected != value) {
_selectedCount += value ? 1 : -1;
assert(_selectedCount >= 0);
dessert.selected = value;
notifyListeners();
}
},
cells: [
DataCell(Text(dessert.name)),
DataCell(Text('${dessert.calories}')),
DataCell(Text(dessert.fat.toStringAsFixed(1))),
DataCell(Text('${dessert.carbs}')),
DataCell(Text(dessert.protein.toStringAsFixed(1))),
DataCell(Text('${dessert.sodium}')),
DataCell(Text('${format.format(dessert.calcium / 100)}')),
DataCell(Text('${format.format(dessert.iron / 100)}')),
],
);
}
@override
int get rowCount => _desserts.length;
@override
bool get isRowCountApproximate => false;
@override
int get selectedRowCount => _selectedCount;
void _selectAll(bool checked) {
for (final _Dessert dessert in _desserts) {
dessert.selected = checked;
}
_selectedCount = checked ? _desserts.length : 0;
notifyListeners();
}
}
// END

View File

@@ -0,0 +1,285 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/data/gallery_options.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN dialogDemo
enum DialogDemoType {
alert,
alertTitle,
simple,
fullscreen,
}
class DialogDemo extends StatelessWidget {
DialogDemo({Key key, @required this.type}) : super(key: key);
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final DialogDemoType type;
String _title(BuildContext context) {
switch (type) {
case DialogDemoType.alert:
return GalleryLocalizations.of(context).demoAlertDialogTitle;
case DialogDemoType.alertTitle:
return GalleryLocalizations.of(context).demoAlertTitleDialogTitle;
case DialogDemoType.simple:
return GalleryLocalizations.of(context).demoSimpleDialogTitle;
case DialogDemoType.fullscreen:
return GalleryLocalizations.of(context).demoFullscreenDialogTitle;
}
return '';
}
Future<void> _showDemoDialog<T>({BuildContext context, Widget child}) async {
child = ApplyTextOptions(
child: Theme(
data: Theme.of(context),
child: child,
),
);
T value = await showDialog<T>(
context: context,
builder: (context) => child,
);
// The value passed to Navigator.pop() or null.
if (value != null && value is String) {
_scaffoldKey.currentState.hideCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(SnackBar(
content:
Text(GalleryLocalizations.of(context).dialogSelectedOption(value)),
));
}
}
void _showAlertDialog(BuildContext context) {
final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle =
theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color);
_showDemoDialog<String>(
context: context,
child: AlertDialog(
content: Text(
GalleryLocalizations.of(context).dialogDiscardTitle,
style: dialogTextStyle,
),
actions: [
_DialogButton(text: GalleryLocalizations.of(context).dialogCancel),
_DialogButton(text: GalleryLocalizations.of(context).dialogDiscard),
],
),
);
}
void _showAlertDialogWithTitle(BuildContext context) {
final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle =
theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color);
_showDemoDialog<String>(
context: context,
child: AlertDialog(
title: Text(GalleryLocalizations.of(context).dialogLocationTitle),
content: Text(
GalleryLocalizations.of(context).dialogLocationDescription,
style: dialogTextStyle,
),
actions: [
_DialogButton(text: GalleryLocalizations.of(context).dialogDisagree),
_DialogButton(text: GalleryLocalizations.of(context).dialogAgree),
],
),
);
}
void _showSimpleDialog(BuildContext context) {
final ThemeData theme = Theme.of(context);
_showDemoDialog<String>(
context: context,
child: SimpleDialog(
title: Text(GalleryLocalizations.of(context).dialogSetBackup),
children: [
_DialogDemoItem(
icon: Icons.account_circle,
color: theme.colorScheme.primary,
text: 'username@gmail.com',
),
_DialogDemoItem(
icon: Icons.account_circle,
color: theme.colorScheme.secondary,
text: 'user02@gmail.com',
),
_DialogDemoItem(
icon: Icons.add_circle,
text: GalleryLocalizations.of(context).dialogAddAccount,
color: theme.disabledColor,
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Navigator(
// Adding [ValueKey] to make sure that the widget gets rebuilt when
// changing type.
key: ValueKey(type),
onGenerateRoute: (settings) {
return _NoAnimationMaterialPageRoute<void>(
builder: (context) => Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: Center(
child: RaisedButton(
child: Text(GalleryLocalizations.of(context).dialogShow),
onPressed: () {
switch (type) {
case DialogDemoType.alert:
_showAlertDialog(context);
break;
case DialogDemoType.alertTitle:
_showAlertDialogWithTitle(context);
break;
case DialogDemoType.simple:
_showSimpleDialog(context);
break;
case DialogDemoType.fullscreen:
Navigator.push<void>(
context,
MaterialPageRoute(
builder: (context) => _FullScreenDialogDemo(),
fullscreenDialog: true,
),
);
break;
}
},
),
),
),
);
},
);
}
}
/// A MaterialPageRoute without any transition animations.
class _NoAnimationMaterialPageRoute<T> extends MaterialPageRoute<T> {
_NoAnimationMaterialPageRoute({
@required WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
}) : super(
builder: builder,
maintainState: maintainState,
settings: settings,
fullscreenDialog: fullscreenDialog);
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return child;
}
}
class _DialogButton extends StatelessWidget {
const _DialogButton({Key key, this.text}) : super(key: key);
final String text;
@override
Widget build(BuildContext context) {
return FlatButton(
child: Text(text),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop(text);
},
);
}
}
class _DialogDemoItem extends StatelessWidget {
const _DialogDemoItem({
Key key,
this.icon,
this.color,
this.text,
}) : super(key: key);
final IconData icon;
final Color color;
final String text;
@override
Widget build(BuildContext context) {
return SimpleDialogOption(
onPressed: () {
Navigator.of(context, rootNavigator: true).pop(text);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(icon, size: 36, color: color),
Flexible(
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 16),
child: Text(text),
),
),
],
),
);
}
}
class _FullScreenDialogDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
// Remove the MediaQuery padding because the demo is rendered inside of a
// different page that already accounts for this padding.
return MediaQuery.removePadding(
context: context,
removeTop: true,
removeBottom: true,
child: ApplyTextOptions(
child: Scaffold(
appBar: AppBar(
title: Text(GalleryLocalizations.of(context).dialogFullscreenTitle),
actions: [
FlatButton(
child: Text(
GalleryLocalizations.of(context).dialogFullscreenSave,
style: theme.textTheme.body1.copyWith(
color: theme.colorScheme.onPrimary,
),
),
onPressed: () {
Navigator.pop(context);
},
),
],
),
body: Center(
child: Text(
GalleryLocalizations.of(context).dialogFullscreenDescription,
),
),
),
),
);
}
}
// END

View File

@@ -0,0 +1,199 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN gridListsDemo
enum GridListDemoType {
imageOnly,
header,
footer,
}
class GridListDemo extends StatelessWidget {
const GridListDemo({Key key, this.type}) : super(key: key);
final GridListDemoType type;
List<_Photo> _photos(BuildContext context) {
return [
_Photo(
assetName: 'places/india_chennai_flower_market.png',
title: GalleryLocalizations.of(context).placeChennai,
subtitle: GalleryLocalizations.of(context).placeFlowerMarket,
),
_Photo(
assetName: 'places/india_tanjore_bronze_works.png',
title: GalleryLocalizations.of(context).placeTanjore,
subtitle: GalleryLocalizations.of(context).placeBronzeWorks,
),
_Photo(
assetName: 'places/india_tanjore_market_merchant.png',
title: GalleryLocalizations.of(context).placeTanjore,
subtitle: GalleryLocalizations.of(context).placeMarket,
),
_Photo(
assetName: 'places/india_tanjore_thanjavur_temple.png',
title: GalleryLocalizations.of(context).placeTanjore,
subtitle: GalleryLocalizations.of(context).placeThanjavurTemple,
),
_Photo(
assetName: 'places/india_tanjore_thanjavur_temple_carvings.png',
title: GalleryLocalizations.of(context).placeTanjore,
subtitle: GalleryLocalizations.of(context).placeThanjavurTemple,
),
_Photo(
assetName: 'places/india_pondicherry_salt_farm.png',
title: GalleryLocalizations.of(context).placePondicherry,
subtitle: GalleryLocalizations.of(context).placeSaltFarm,
),
_Photo(
assetName: 'places/india_chennai_highway.png',
title: GalleryLocalizations.of(context).placeChennai,
subtitle: GalleryLocalizations.of(context).placeScooters,
),
_Photo(
assetName: 'places/india_chettinad_silk_maker.png',
title: GalleryLocalizations.of(context).placeChettinad,
subtitle: GalleryLocalizations.of(context).placeSilkMaker,
),
_Photo(
assetName: 'places/india_chettinad_produce.png',
title: GalleryLocalizations.of(context).placeChettinad,
subtitle: GalleryLocalizations.of(context).placeLunchPrep,
),
_Photo(
assetName: 'places/india_tanjore_market_technology.png',
title: GalleryLocalizations.of(context).placeTanjore,
subtitle: GalleryLocalizations.of(context).placeMarket,
),
_Photo(
assetName: 'places/india_pondicherry_beach.png',
title: GalleryLocalizations.of(context).placePondicherry,
subtitle: GalleryLocalizations.of(context).placeBeach,
),
_Photo(
assetName: 'places/india_pondicherry_fisherman.png',
title: GalleryLocalizations.of(context).placePondicherry,
subtitle: GalleryLocalizations.of(context).placeFisherman,
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoGridListsTitle),
),
body: GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
padding: const EdgeInsets.all(8),
childAspectRatio: 1,
children: _photos(context).map<Widget>((photo) {
return _GridDemoPhotoItem(
photo: photo,
tileStyle: type,
);
}).toList(),
),
);
}
}
class _Photo {
_Photo({
this.assetName,
this.title,
this.subtitle,
});
final String assetName;
final String title;
final String subtitle;
}
/// Allow the text size to shrink to fit in the space
class _GridTitleText extends StatelessWidget {
const _GridTitleText(this.text);
final String text;
@override
Widget build(BuildContext context) {
return FittedBox(
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.centerStart,
child: Text(text),
);
}
}
class _GridDemoPhotoItem extends StatelessWidget {
_GridDemoPhotoItem({
Key key,
@required this.photo,
@required this.tileStyle,
}) : super(key: key);
final _Photo photo;
final GridListDemoType tileStyle;
@override
Widget build(BuildContext context) {
final Widget image = Material(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
clipBehavior: Clip.antiAlias,
child: Image.asset(
photo.assetName,
package: 'flutter_gallery_assets',
fit: BoxFit.cover,
),
);
switch (tileStyle) {
case GridListDemoType.imageOnly:
return image;
case GridListDemoType.header:
return GridTile(
header: Material(
color: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(4)),
),
clipBehavior: Clip.antiAlias,
child: GridTileBar(
title: _GridTitleText(photo.title),
backgroundColor: Colors.black45,
),
),
child: image,
);
case GridListDemoType.footer:
return GridTile(
footer: Material(
color: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(4)),
),
clipBehavior: Clip.antiAlias,
child: GridTileBar(
backgroundColor: Colors.black45,
title: _GridTitleText(photo.title),
subtitle: _GridTitleText(photo.subtitle),
),
),
child: image,
);
}
return null;
}
}
// END

View File

@@ -0,0 +1,51 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN listDemo
enum ListDemoType {
oneLine,
twoLine,
}
class ListDemo extends StatelessWidget {
const ListDemo({Key key, this.type}) : super(key: key);
final ListDemoType type;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoListsTitle),
),
body: Scrollbar(
child: ListView(
padding: EdgeInsets.symmetric(vertical: 8),
children: [
for (int index = 1; index < 21; index++)
ListTile(
leading: ExcludeSemantics(
child: CircleAvatar(child: Text('$index')),
),
title: Text(
GalleryLocalizations.of(context).demoBottomSheetItem(index),
),
subtitle: type == ListDemoType.twoLine
? Text(GalleryLocalizations.of(context).demoListsSecondary)
: null,
),
],
),
),
);
}
}
// END

View File

@@ -0,0 +1,368 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum MenuDemoType {
contextMenu,
sectionedMenu,
simpleMenu,
checklistMenu,
}
enum SimpleValue {
one,
two,
three,
}
enum CheckedValue {
one,
two,
three,
four,
}
class MenuDemo extends StatefulWidget {
const MenuDemo({Key key, this.type}) : super(key: key);
final MenuDemoType type;
@override
_MenuDemoState createState() => _MenuDemoState();
}
class _MenuDemoState extends State<MenuDemo> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void showInSnackBar(String value) {
_scaffoldKey.currentState.hideCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(value),
));
}
@override
Widget build(BuildContext context) {
Widget demo;
switch (widget.type) {
case MenuDemoType.contextMenu:
demo = _ContextMenuDemo(showInSnackBar: showInSnackBar);
break;
case MenuDemoType.sectionedMenu:
demo = _SectionedMenuDemo(showInSnackBar: showInSnackBar);
break;
case MenuDemoType.simpleMenu:
demo = _SimpleMenuDemo(showInSnackBar: showInSnackBar);
break;
case MenuDemoType.checklistMenu:
demo = _ChecklistMenuDemo(showInSnackBar: showInSnackBar);
break;
}
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(GalleryLocalizations.of(context).demoMenuTitle),
automaticallyImplyLeading: false,
),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Center(
child: demo,
),
),
);
}
}
// BEGIN menuDemoContext
// Pressing the PopupMenuButton on the right of this item shows
// a simple menu with one disabled item. Typically the contents
// of this "contextual menu" would reflect the app's state.
class _ContextMenuDemo extends StatelessWidget {
const _ContextMenuDemo({Key key, this.showInSnackBar}) : super(key: key);
final void Function(String value) showInSnackBar;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(GalleryLocalizations.of(context)
.demoMenuAnItemWithAContextMenuButton),
trailing: PopupMenuButton<String>(
padding: EdgeInsets.zero,
onSelected: (value) => showInSnackBar(
GalleryLocalizations.of(context).demoMenuSelected(value),
),
itemBuilder: (context) => <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: GalleryLocalizations.of(context).demoMenuContextMenuItemOne,
child: Text(
GalleryLocalizations.of(context).demoMenuContextMenuItemOne,
),
),
PopupMenuItem<String>(
enabled: false,
child: Text(
GalleryLocalizations.of(context).demoMenuADisabledMenuItem,
),
),
PopupMenuItem<String>(
value:
GalleryLocalizations.of(context).demoMenuContextMenuItemThree,
child: Text(
GalleryLocalizations.of(context).demoMenuContextMenuItemThree,
),
),
],
),
);
}
}
// END
// BEGIN menuDemoSectioned
// Pressing the PopupMenuButton on the right of this item shows
// a menu whose items have text labels and icons and a divider
// That separates the first three items from the last one.
class _SectionedMenuDemo extends StatelessWidget {
const _SectionedMenuDemo({Key key, this.showInSnackBar}) : super(key: key);
final void Function(String value) showInSnackBar;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
GalleryLocalizations.of(context).demoMenuAnItemWithASectionedMenu),
trailing: PopupMenuButton<String>(
padding: EdgeInsets.zero,
onSelected: (value) => showInSnackBar(
GalleryLocalizations.of(context).demoMenuSelected(value)),
itemBuilder: (context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: GalleryLocalizations.of(context).demoMenuPreview,
child: ListTile(
leading: Icon(Icons.visibility),
title: Text(
GalleryLocalizations.of(context).demoMenuPreview,
),
),
),
PopupMenuItem<String>(
value: GalleryLocalizations.of(context).demoMenuShare,
child: ListTile(
leading: Icon(Icons.person_add),
title: Text(
GalleryLocalizations.of(context).demoMenuShare,
),
),
),
PopupMenuItem<String>(
value: GalleryLocalizations.of(context).demoMenuGetLink,
child: ListTile(
leading: Icon(Icons.link),
title: Text(
GalleryLocalizations.of(context).demoMenuGetLink,
),
),
),
const PopupMenuDivider(),
PopupMenuItem<String>(
value: GalleryLocalizations.of(context).demoMenuRemove,
child: ListTile(
leading: Icon(Icons.delete),
title: Text(
GalleryLocalizations.of(context).demoMenuRemove,
),
),
),
],
),
);
}
}
// END
// BEGIN menuDemoSimple
// This entire list item is a PopupMenuButton. Tapping anywhere shows
// a menu whose current value is highlighted and aligned over the
// list item's center line.
class _SimpleMenuDemo extends StatefulWidget {
const _SimpleMenuDemo({Key key, this.showInSnackBar}) : super(key: key);
final void Function(String value) showInSnackBar;
@override
_SimpleMenuDemoState createState() => _SimpleMenuDemoState();
}
class _SimpleMenuDemoState extends State<_SimpleMenuDemo> {
SimpleValue _simpleValue;
void showAndSetMenuSelection(BuildContext context, SimpleValue value) {
setState(() {
_simpleValue = value;
});
widget.showInSnackBar(
GalleryLocalizations.of(context)
.demoMenuSelected(simpleValueToString(context, value)),
);
}
String simpleValueToString(BuildContext context, SimpleValue value) => {
SimpleValue.one: GalleryLocalizations.of(context).demoMenuItemValueOne,
SimpleValue.two: GalleryLocalizations.of(context).demoMenuItemValueTwo,
SimpleValue.three:
GalleryLocalizations.of(context).demoMenuItemValueThree,
}[value];
@override
void initState() {
super.initState();
_simpleValue = SimpleValue.two;
}
@override
Widget build(BuildContext context) {
return PopupMenuButton<SimpleValue>(
padding: EdgeInsets.zero,
initialValue: _simpleValue,
onSelected: (value) => showAndSetMenuSelection(context, value),
child: ListTile(
title: Text(
GalleryLocalizations.of(context).demoMenuAnItemWithASimpleMenu),
subtitle: Text(simpleValueToString(context, _simpleValue)),
),
itemBuilder: (context) => <PopupMenuItem<SimpleValue>>[
PopupMenuItem<SimpleValue>(
value: SimpleValue.one,
child: Text(simpleValueToString(
context,
SimpleValue.one,
)),
),
PopupMenuItem<SimpleValue>(
value: SimpleValue.two,
child: Text(simpleValueToString(
context,
SimpleValue.two,
)),
),
PopupMenuItem<SimpleValue>(
value: SimpleValue.three,
child: Text(simpleValueToString(
context,
SimpleValue.three,
)),
),
],
);
}
}
// END
// BEGIN menuDemoChecklist
// Pressing the PopupMenuButton on the right of this item shows a menu
// whose items have checked icons that reflect this app's state.
class _ChecklistMenuDemo extends StatefulWidget {
const _ChecklistMenuDemo({Key key, this.showInSnackBar}) : super(key: key);
final void Function(String value) showInSnackBar;
@override
_ChecklistMenuDemoState createState() => _ChecklistMenuDemoState();
}
class _ChecklistMenuDemoState extends State<_ChecklistMenuDemo> {
List<CheckedValue> _checkedValues;
@override
void initState() {
super.initState();
_checkedValues = [CheckedValue.three];
}
void showCheckedMenuSelections(BuildContext context, CheckedValue value) {
if (_checkedValues.contains(value)) {
setState(() {
_checkedValues.remove(value);
});
} else {
setState(() {
_checkedValues.add(value);
});
}
widget.showInSnackBar(
GalleryLocalizations.of(context).demoMenuChecked(
_checkedValues.map((value) => checkedValueToString(context, value)),
),
);
}
String checkedValueToString(BuildContext context, CheckedValue value) => {
CheckedValue.one: GalleryLocalizations.of(context).demoMenuOne,
CheckedValue.two: GalleryLocalizations.of(context).demoMenuTwo,
CheckedValue.three: GalleryLocalizations.of(context).demoMenuThree,
CheckedValue.four: GalleryLocalizations.of(context).demoMenuFour,
}[value];
bool isChecked(CheckedValue value) => _checkedValues.contains(value);
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
GalleryLocalizations.of(context).demoMenuAnItemWithAChecklistMenu),
trailing: PopupMenuButton<CheckedValue>(
padding: EdgeInsets.zero,
onSelected: (value) => showCheckedMenuSelections(context, value),
itemBuilder: (context) => <PopupMenuItem<CheckedValue>>[
CheckedPopupMenuItem<CheckedValue>(
value: CheckedValue.one,
checked: isChecked(CheckedValue.one),
child: Text(
checkedValueToString(context, CheckedValue.one),
),
),
CheckedPopupMenuItem<CheckedValue>(
value: CheckedValue.two,
enabled: false,
checked: isChecked(CheckedValue.two),
child: Text(
checkedValueToString(context, CheckedValue.two),
),
),
CheckedPopupMenuItem<CheckedValue>(
value: CheckedValue.three,
checked: isChecked(CheckedValue.three),
child: Text(
checkedValueToString(context, CheckedValue.three),
),
),
CheckedPopupMenuItem<CheckedValue>(
value: CheckedValue.four,
checked: isChecked(CheckedValue.four),
child: Text(
checkedValueToString(context, CheckedValue.four),
),
),
],
),
);
}
}
// END

View File

@@ -0,0 +1,112 @@
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
import 'package:intl/intl.dart';
// BEGIN pickerDemo
enum PickerDemoType {
date,
time,
}
class PickerDemo extends StatefulWidget {
const PickerDemo({Key key, this.type}) : super(key: key);
final PickerDemoType type;
@override
_PickerDemoState createState() => _PickerDemoState();
}
class _PickerDemoState extends State<PickerDemo> {
DateTime _fromDate = DateTime.now();
TimeOfDay _fromTime = TimeOfDay.fromDateTime(DateTime.now());
String get _title {
switch (widget.type) {
case PickerDemoType.date:
return GalleryLocalizations.of(context).demoDatePickerTitle;
case PickerDemoType.time:
return GalleryLocalizations.of(context).demoTimePickerTitle;
}
return '';
}
String get _labelText {
switch (widget.type) {
case PickerDemoType.date:
return DateFormat.yMMMd().format(_fromDate);
case PickerDemoType.time:
return _fromTime.format(context);
}
return '';
}
Future<void> _showDatePicker() async {
final picked = await showDatePicker(
context: context,
initialDate: _fromDate,
firstDate: DateTime(2015, 1),
lastDate: DateTime(2100),
);
if (picked != null && picked != _fromDate) {
setState(() {
_fromDate = picked;
});
}
}
Future<void> _showTimePicker() async {
final TimeOfDay picked = await showTimePicker(
context: context,
initialTime: _fromTime,
);
if (picked != null && picked != _fromTime) {
setState(() {
_fromTime = picked;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(_labelText),
SizedBox(height: 16),
RaisedButton(
child: Text(
GalleryLocalizations.of(context).demoPickersShowPicker,
),
onPressed: () {
switch (widget.type) {
case PickerDemoType.date:
_showDatePicker();
break;
case PickerDemoType.time:
_showTimePicker();
break;
}
},
)
],
),
),
);
}
}
// END

View File

@@ -0,0 +1,114 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN progressIndicatorsDemo
enum ProgressIndicatorDemoType {
circular,
linear,
}
class ProgressIndicatorDemo extends StatefulWidget {
const ProgressIndicatorDemo({Key key, this.type}) : super(key: key);
final ProgressIndicatorDemoType type;
@override
_ProgressIndicatorDemoState createState() => _ProgressIndicatorDemoState();
}
class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
animationBehavior: AnimationBehavior.preserve,
)..forward();
_animation = CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.9, curve: Curves.fastOutSlowIn),
reverseCurve: Curves.fastOutSlowIn,
)..addStatusListener((status) {
if (status == AnimationStatus.dismissed) {
_controller.forward();
} else if (status == AnimationStatus.completed) {
_controller.reverse();
}
});
}
@override
void dispose() {
_controller.stop();
super.dispose();
}
String get _title {
switch (widget.type) {
case ProgressIndicatorDemoType.circular:
return GalleryLocalizations.of(context)
.demoCircularProgressIndicatorTitle;
case ProgressIndicatorDemoType.linear:
return GalleryLocalizations.of(context)
.demoLinearProgressIndicatorTitle;
}
return '';
}
Widget _buildIndicators(BuildContext context, Widget child) {
switch (widget.type) {
case ProgressIndicatorDemoType.circular:
return Column(
children: [
const CircularProgressIndicator(),
SizedBox(height: 32),
CircularProgressIndicator(value: _animation.value),
],
);
case ProgressIndicatorDemoType.linear:
return Column(
children: [
const LinearProgressIndicator(),
SizedBox(height: 32),
LinearProgressIndicator(value: _animation.value),
],
);
default:
return Container();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title),
),
body: Center(
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(8),
child: AnimatedBuilder(
animation: _animation,
builder: _buildIndicators,
),
),
),
),
);
}
}
// END

View File

@@ -0,0 +1,176 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum SelectionControlsDemoType {
checkbox,
radio,
switches,
}
class SelectionControlsDemo extends StatelessWidget {
SelectionControlsDemo({Key key, @required this.type}) : super(key: key);
final SelectionControlsDemoType type;
String _title(BuildContext context) {
switch (type) {
case SelectionControlsDemoType.checkbox:
return GalleryLocalizations.of(context)
.demoSelectionControlsCheckboxTitle;
case SelectionControlsDemoType.radio:
return GalleryLocalizations.of(context).demoSelectionControlsRadioTitle;
case SelectionControlsDemoType.switches:
return GalleryLocalizations.of(context)
.demoSelectionControlsSwitchTitle;
}
return '';
}
@override
Widget build(BuildContext context) {
Widget controls;
switch (type) {
case SelectionControlsDemoType.checkbox:
controls = _CheckboxDemo();
break;
case SelectionControlsDemoType.radio:
controls = _RadioDemo();
break;
case SelectionControlsDemoType.switches:
controls = _SwitchDemo();
break;
}
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: controls,
);
}
}
// BEGIN selectionControlsDemoCheckbox
class _CheckboxDemo extends StatefulWidget {
@override
_CheckboxDemoState createState() => _CheckboxDemoState();
}
class _CheckboxDemoState extends State<_CheckboxDemo> {
bool checkboxValueA = true;
bool checkboxValueB = false;
bool checkboxValueC;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: checkboxValueA,
onChanged: (value) {
setState(() {
checkboxValueA = value;
});
},
),
Checkbox(
value: checkboxValueB,
onChanged: (value) {
setState(() {
checkboxValueB = value;
});
},
),
Checkbox(
value: checkboxValueC,
tristate: true,
onChanged: (value) {
setState(() {
checkboxValueC = value;
});
},
),
],
),
);
}
}
// END
// BEGIN selectionControlsDemoRadio
class _RadioDemo extends StatefulWidget {
@override
_RadioDemoState createState() => _RadioDemoState();
}
class _RadioDemoState extends State<_RadioDemo> {
int radioValue = 0;
void handleRadioValueChanged(int value) {
setState(() {
radioValue = value;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
for (int index = 0; index < 3; ++index)
Radio<int>(
value: index,
groupValue: radioValue,
onChanged: handleRadioValueChanged,
),
],
),
);
}
}
// END
// BEGIN selectionControlsDemoSwitches
class _SwitchDemo extends StatefulWidget {
@override
_SwitchDemoState createState() => _SwitchDemoState();
}
class _SwitchDemoState extends State<_SwitchDemo> {
bool switchValue = false;
@override
Widget build(BuildContext context) {
return Center(
child: Semantics(
container: true,
label:
GalleryLocalizations.of(context).demoSelectionControlsSwitchTitle,
child: Switch(
value: switchValue,
onChanged: (value) {
setState(() {
switchValue = value;
});
},
),
),
);
}
}
// END

View File

@@ -0,0 +1,503 @@
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum SlidersDemoType {
sliders,
rangeSliders,
customSliders,
}
class SlidersDemo extends StatelessWidget {
const SlidersDemo({Key key, this.type}) : super(key: key);
final SlidersDemoType type;
String _title(BuildContext context) {
switch (type) {
case SlidersDemoType.sliders:
return GalleryLocalizations.of(context).demoSlidersTitle;
case SlidersDemoType.rangeSliders:
return GalleryLocalizations.of(context).demoRangeSlidersTitle;
case SlidersDemoType.customSliders:
return GalleryLocalizations.of(context).demoCustomSlidersTitle;
}
return '';
}
@override
Widget build(BuildContext context) {
Widget sliders;
switch (type) {
case SlidersDemoType.sliders:
sliders = _Sliders();
break;
case SlidersDemoType.rangeSliders:
sliders = _RangeSliders();
break;
case SlidersDemoType.customSliders:
sliders = _CustomSliders();
}
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(_title(context)),
),
body: sliders,
);
}
}
// BEGIN slidersDemo
class _Sliders extends StatefulWidget {
@override
_SlidersState createState() => _SlidersState();
}
class _SlidersState extends State<_Sliders> {
double _continuousValue = 25;
double _discreteValue = 20;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Semantics(
label: GalleryLocalizations.of(context)
.demoSlidersEditableNumericalValue,
child: SizedBox(
width: 64,
height: 48,
child: TextField(
textAlign: TextAlign.center,
onSubmitted: (value) {
final double newValue = double.tryParse(value);
if (newValue != null && newValue != _continuousValue) {
setState(() {
_continuousValue = newValue.clamp(0, 100) as double;
});
}
},
keyboardType: TextInputType.number,
controller: TextEditingController(
text: _continuousValue.toStringAsFixed(0),
),
),
),
),
Slider(
value: _continuousValue,
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_continuousValue = value;
});
},
),
Text(GalleryLocalizations.of(context)
.demoSlidersContinuousWithEditableNumericalValue),
],
),
const SizedBox(height: 80),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Slider(
value: _discreteValue,
min: 0,
max: 200,
divisions: 5,
label: _discreteValue.round().toString(),
onChanged: (value) {
setState(() {
_discreteValue = value;
});
},
),
Text(GalleryLocalizations.of(context).demoSlidersDiscrete),
],
),
],
),
);
}
}
// END
// BEGIN rangeSlidersDemo
class _RangeSliders extends StatefulWidget {
@override
_RangeSlidersState createState() => _RangeSlidersState();
}
class _RangeSlidersState extends State<_RangeSliders> {
RangeValues _continuousValues = const RangeValues(25, 75);
RangeValues _discreteValues = const RangeValues(40, 120);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
RangeSlider(
values: _continuousValues,
min: 0,
max: 100,
onChanged: (values) {
setState(() {
_continuousValues = values;
});
},
),
Text(GalleryLocalizations.of(context).demoSlidersContinuous),
],
),
const SizedBox(height: 80),
Column(
mainAxisSize: MainAxisSize.min,
children: [
RangeSlider(
values: _discreteValues,
min: 0,
max: 200,
divisions: 5,
labels: RangeLabels(
_discreteValues.start.round().toString(),
_discreteValues.end.round().toString(),
),
onChanged: (values) {
setState(() {
_discreteValues = values;
});
},
),
Text(GalleryLocalizations.of(context).demoSlidersDiscrete),
],
),
],
),
);
}
}
// END
// BEGIN customSlidersDemo
Path _downTriangle(double size, Offset thumbCenter, {bool invert = false}) {
final thumbPath = Path();
final height = math.sqrt(3) / 2;
final centerHeight = size * height / 3;
final halfSize = size / 2;
final sign = invert ? -1 : 1;
thumbPath.moveTo(
thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight);
thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2 * sign * centerHeight);
thumbPath.lineTo(
thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight);
thumbPath.close();
return thumbPath;
}
Path _rightTriangle(double size, Offset thumbCenter, {bool invert = false}) {
final thumbPath = Path();
final halfSize = size / 2;
final sign = invert ? -1 : 1;
thumbPath.moveTo(thumbCenter.dx + halfSize * sign, thumbCenter.dy);
thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy - size);
thumbPath.lineTo(thumbCenter.dx - halfSize * sign, thumbCenter.dy + size);
thumbPath.close();
return thumbPath;
}
Path _upTriangle(double size, Offset thumbCenter) =>
_downTriangle(size, thumbCenter, invert: true);
Path _leftTriangle(double size, Offset thumbCenter) =>
_rightTriangle(size, thumbCenter, invert: true);
class _CustomRangeThumbShape extends RangeSliderThumbShape {
static const double _thumbSize = 4;
static const double _disabledThumbSize = 3;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return isEnabled
? const Size.fromRadius(_thumbSize)
: const Size.fromRadius(_disabledThumbSize);
}
static final Animatable<double> sizeTween = Tween<double>(
begin: _disabledThumbSize,
end: _thumbSize,
);
@override
void paint(
PaintingContext context,
Offset center, {
@required Animation<double> activationAnimation,
@required Animation<double> enableAnimation,
bool isDiscrete = false,
bool isEnabled = false,
bool isOnTop,
@required SliderThemeData sliderTheme,
TextDirection textDirection,
Thumb thumb,
}) {
final canvas = context.canvas;
final colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final size = _thumbSize * sizeTween.evaluate(enableAnimation);
Path thumbPath;
switch (textDirection) {
case TextDirection.rtl:
switch (thumb) {
case Thumb.start:
thumbPath = _rightTriangle(size, center);
break;
case Thumb.end:
thumbPath = _leftTriangle(size, center);
break;
}
break;
case TextDirection.ltr:
switch (thumb) {
case Thumb.start:
thumbPath = _leftTriangle(size, center);
break;
case Thumb.end:
thumbPath = _rightTriangle(size, center);
break;
}
break;
}
canvas.drawPath(
thumbPath,
Paint()..color = colorTween.evaluate(enableAnimation),
);
}
}
class _CustomThumbShape extends SliderComponentShape {
static const double _thumbSize = 4;
static const double _disabledThumbSize = 3;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return isEnabled
? const Size.fromRadius(_thumbSize)
: const Size.fromRadius(_disabledThumbSize);
}
static final Animatable<double> sizeTween = Tween<double>(
begin: _disabledThumbSize,
end: _thumbSize,
);
@override
void paint(
PaintingContext context,
Offset thumbCenter, {
Animation<double> activationAnimation,
Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme,
TextDirection textDirection,
double value,
}) {
final canvas = context.canvas;
final colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final size = _thumbSize * sizeTween.evaluate(enableAnimation);
final thumbPath = _downTriangle(size, thumbCenter);
canvas.drawPath(
thumbPath,
Paint()..color = colorTween.evaluate(enableAnimation),
);
}
}
class _CustomValueIndicatorShape extends SliderComponentShape {
static const double _indicatorSize = 4;
static const double _disabledIndicatorSize = 3;
static const double _slideUpHeight = 40;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);
}
static final Animatable<double> sizeTween = Tween<double>(
begin: _disabledIndicatorSize,
end: _indicatorSize,
);
@override
void paint(
PaintingContext context,
Offset thumbCenter, {
Animation<double> activationAnimation,
Animation<double> enableAnimation,
bool isDiscrete,
TextPainter labelPainter,
RenderBox parentBox,
SliderThemeData sliderTheme,
TextDirection textDirection,
double value,
}) {
final canvas = context.canvas;
final enableColor = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.valueIndicatorColor,
);
final slideUpTween = Tween<double>(
begin: 0,
end: _slideUpHeight,
);
final size = _indicatorSize * sizeTween.evaluate(enableAnimation);
final slideUpOffset =
Offset(0, -slideUpTween.evaluate(activationAnimation));
final thumbPath = _upTriangle(size, thumbCenter + slideUpOffset);
final paintColor = enableColor
.evaluate(enableAnimation)
.withAlpha((255 * activationAnimation.value).round());
canvas.drawPath(
thumbPath,
Paint()..color = paintColor,
);
canvas.drawLine(
thumbCenter,
thumbCenter + slideUpOffset,
Paint()
..color = paintColor
..style = PaintingStyle.stroke
..strokeWidth = 2);
labelPainter.paint(
canvas,
thumbCenter +
slideUpOffset +
Offset(-labelPainter.width / 2, -labelPainter.height - 4),
);
}
}
class _CustomSliders extends StatefulWidget {
@override
_CustomSlidersState createState() => _CustomSlidersState();
}
class _CustomSlidersState extends State<_CustomSliders> {
double _discreteCustomValue = 25;
RangeValues _continuousCustomValues = const RangeValues(40, 160);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
SliderTheme(
data: theme.sliderTheme.copyWith(
activeTrackColor: Colors.deepPurple,
inactiveTrackColor:
theme.colorScheme.onSurface.withOpacity(0.5),
activeTickMarkColor:
theme.colorScheme.onSurface.withOpacity(0.7),
inactiveTickMarkColor:
theme.colorScheme.surface.withOpacity(0.7),
overlayColor: theme.colorScheme.onSurface.withOpacity(0.12),
thumbColor: Colors.deepPurple,
valueIndicatorColor: Colors.deepPurpleAccent,
thumbShape: _CustomThumbShape(),
valueIndicatorShape: _CustomValueIndicatorShape(),
valueIndicatorTextStyle: theme.accentTextTheme.body2
.copyWith(color: theme.colorScheme.onSurface),
),
child: Slider(
value: _discreteCustomValue,
min: 0,
max: 200,
divisions: 5,
semanticFormatterCallback: (value) =>
value.round().toString(),
label: '${_discreteCustomValue.round()}',
onChanged: (value) {
setState(() {
_discreteCustomValue = value;
});
},
),
),
Text(GalleryLocalizations.of(context)
.demoSlidersDiscreteSliderWithCustomTheme),
],
),
const SizedBox(height: 80),
Column(
mainAxisSize: MainAxisSize.min,
children: [
SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.deepPurple,
inactiveTrackColor: Colors.black26,
activeTickMarkColor: Colors.white70,
inactiveTickMarkColor: Colors.black,
overlayColor: Colors.black12,
thumbColor: Colors.deepPurple,
rangeThumbShape: _CustomRangeThumbShape(),
showValueIndicator: ShowValueIndicator.never,
),
child: RangeSlider(
values: _continuousCustomValues,
min: 0,
max: 200,
onChanged: (values) {
setState(() {
_continuousCustomValues = values;
});
},
),
),
Text(GalleryLocalizations.of(context)
.demoSlidersContinuousRangeSliderWithCustomTheme),
],
),
],
),
);
}
}
// END

View File

@@ -0,0 +1,54 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN snackbarsDemo
class SnackbarsDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoSnackbarsTitle),
),
body: Builder(
// Create an inner BuildContext so that the snackBar onPressed methods
// can refer to the Scaffold with Scaffold.of().
builder: (context) {
return Center(
child: RaisedButton(
child: Text(
GalleryLocalizations.of(context).demoSnackbarsButtonLabel),
onPressed: () {
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
GalleryLocalizations.of(context).demoSnackbarsText,
),
action: SnackBarAction(
label: GalleryLocalizations.of(context)
.demoSnackbarsActionButtonLabel,
onPressed: () {
Scaffold.of(context).hideCurrentSnackBar();
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
GalleryLocalizations.of(context).demoSnackbarsAction,
),
));
},
),
));
},
),
);
},
),
);
}
}
// END

View File

@@ -0,0 +1,118 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
enum TabsDemoType {
scrollable,
nonScrollable,
}
class TabsDemo extends StatelessWidget {
const TabsDemo({Key key, this.type}) : super(key: key);
final TabsDemoType type;
@override
Widget build(BuildContext context) {
Widget tabs;
switch (type) {
case TabsDemoType.scrollable:
tabs = _TabsScrollableDemo();
break;
case TabsDemoType.nonScrollable:
tabs = _TabsNonScrollableDemo();
}
return tabs;
}
}
// BEGIN tabsScrollableDemo
class _TabsScrollableDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<String> tabs = [
GalleryLocalizations.of(context).colorsRed,
GalleryLocalizations.of(context).colorsOrange,
GalleryLocalizations.of(context).colorsGreen,
GalleryLocalizations.of(context).colorsBlue,
GalleryLocalizations.of(context).colorsIndigo,
GalleryLocalizations.of(context).colorsPurple,
GalleryLocalizations.of(context).colorsRed,
GalleryLocalizations.of(context).colorsOrange,
GalleryLocalizations.of(context).colorsGreen,
GalleryLocalizations.of(context).colorsBlue,
GalleryLocalizations.of(context).colorsIndigo,
GalleryLocalizations.of(context).colorsPurple,
];
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoTabsScrollingTitle),
bottom: TabBar(
isScrollable: true,
tabs: [
for (final tab in tabs) Tab(text: tab),
],
),
),
body: TabBarView(
children: [
for (final tab in tabs)
Center(
child: Text(tab),
),
],
),
),
);
}
}
// END
// BEGIN tabsNonScrollableDemo
class _TabsNonScrollableDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
List<String> tabs = [
GalleryLocalizations.of(context).colorsRed,
GalleryLocalizations.of(context).colorsOrange,
GalleryLocalizations.of(context).colorsGreen,
];
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title:
Text(GalleryLocalizations.of(context).demoTabsNonScrollingTitle),
bottom: TabBar(
isScrollable: false,
tabs: [
for (final tab in tabs) Tab(text: tab),
],
),
),
body: TabBarView(
children: [
for (final tab in tabs)
Center(
child: Text(tab),
),
],
),
),
);
}
}
// END

View File

@@ -0,0 +1,357 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN textFieldDemo
class TextFieldDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoTextFieldTitle),
),
body: TextFormFieldDemo(),
);
}
}
class TextFormFieldDemo extends StatefulWidget {
const TextFormFieldDemo({Key key}) : super(key: key);
@override
TextFormFieldDemoState createState() => TextFormFieldDemoState();
}
class PersonData {
String name = '';
String phoneNumber = '';
String email = '';
String password = '';
}
class PasswordField extends StatefulWidget {
const PasswordField({
this.fieldKey,
this.hintText,
this.labelText,
this.helperText,
this.onSaved,
this.validator,
this.onFieldSubmitted,
});
final Key fieldKey;
final String hintText;
final String labelText;
final String helperText;
final FormFieldSetter<String> onSaved;
final FormFieldValidator<String> validator;
final ValueChanged<String> onFieldSubmitted;
@override
_PasswordFieldState createState() => _PasswordFieldState();
}
class _PasswordFieldState extends State<PasswordField> {
bool _obscureText = true;
@override
Widget build(BuildContext context) {
return TextFormField(
key: widget.fieldKey,
obscureText: _obscureText,
cursorColor: Theme.of(context).cursorColor,
maxLength: 8,
onSaved: widget.onSaved,
validator: widget.validator,
onFieldSubmitted: widget.onFieldSubmitted,
decoration: InputDecoration(
filled: true,
hintText: widget.hintText,
labelText: widget.labelText,
helperText: widget.helperText,
suffixIcon: GestureDetector(
dragStartBehavior: DragStartBehavior.down,
onTap: () {
setState(() {
_obscureText = !_obscureText;
});
},
child: Icon(
_obscureText ? Icons.visibility : Icons.visibility_off,
semanticLabel: _obscureText
? GalleryLocalizations.of(context)
.demoTextFieldShowPasswordLabel
: GalleryLocalizations.of(context)
.demoTextFieldHidePasswordLabel,
),
),
),
);
}
}
class TextFormFieldDemoState extends State<TextFormFieldDemo> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
PersonData person = PersonData();
void showInSnackBar(String value) {
_scaffoldKey.currentState.hideCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(value),
));
}
bool _autoValidate = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<FormFieldState<String>> _passwordFieldKey =
GlobalKey<FormFieldState<String>>();
final _UsNumberTextInputFormatter _phoneNumberFormatter =
_UsNumberTextInputFormatter();
void _handleSubmitted() {
final form = _formKey.currentState;
if (!form.validate()) {
_autoValidate = true; // Start validating on every change.
showInSnackBar(
GalleryLocalizations.of(context).demoTextFieldFormErrors,
);
} else {
form.save();
showInSnackBar(GalleryLocalizations.of(context)
.demoTextFieldNameHasPhoneNumber(person.name, person.phoneNumber));
}
}
String _validateName(String value) {
if (value.isEmpty) {
return GalleryLocalizations.of(context).demoTextFieldNameRequired;
}
final nameExp = RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value)) {
return GalleryLocalizations.of(context)
.demoTextFieldOnlyAlphabeticalChars;
}
return null;
}
String _validatePhoneNumber(String value) {
final phoneExp = RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
if (!phoneExp.hasMatch(value)) {
return GalleryLocalizations.of(context).demoTextFieldEnterUSPhoneNumber;
}
return null;
}
String _validatePassword(String value) {
final passwordField = _passwordFieldKey.currentState;
if (passwordField.value == null || passwordField.value.isEmpty) {
return GalleryLocalizations.of(context).demoTextFieldEnterPassword;
}
if (passwordField.value != value) {
return GalleryLocalizations.of(context).demoTextFieldPasswordsDoNotMatch;
}
return null;
}
@override
Widget build(BuildContext context) {
final cursorColor = Theme.of(context).cursorColor;
const sizedBoxSpace = SizedBox(height: 24);
return Scaffold(
key: _scaffoldKey,
body: Form(
key: _formKey,
autovalidate: _autoValidate,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
sizedBoxSpace,
TextFormField(
textCapitalization: TextCapitalization.words,
cursorColor: cursorColor,
decoration: InputDecoration(
filled: true,
icon: Icon(Icons.person),
hintText: GalleryLocalizations.of(context)
.demoTextFieldWhatDoPeopleCallYou,
labelText:
GalleryLocalizations.of(context).demoTextFieldNameField,
),
onSaved: (value) {
person.name = value;
},
validator: _validateName,
),
sizedBoxSpace,
TextFormField(
cursorColor: cursorColor,
decoration: InputDecoration(
filled: true,
icon: Icon(Icons.phone),
hintText: GalleryLocalizations.of(context)
.demoTextFieldWhereCanWeReachYou,
labelText: GalleryLocalizations.of(context)
.demoTextFieldPhoneNumber,
prefixText: '+1 ',
),
keyboardType: TextInputType.phone,
onSaved: (value) {
person.phoneNumber = value;
},
maxLength: 14,
maxLengthEnforced: false,
validator: _validatePhoneNumber,
// TextInputFormatters are applied in sequence.
inputFormatters: <TextInputFormatter>[
WhitelistingTextInputFormatter.digitsOnly,
// Fit the validating format.
_phoneNumberFormatter,
],
),
sizedBoxSpace,
TextFormField(
cursorColor: cursorColor,
decoration: InputDecoration(
filled: true,
icon: Icon(Icons.email),
hintText: GalleryLocalizations.of(context)
.demoTextFieldYourEmailAddress,
labelText:
GalleryLocalizations.of(context).demoTextFieldEmail,
),
keyboardType: TextInputType.emailAddress,
onSaved: (value) {
person.email = value;
},
),
sizedBoxSpace,
TextFormField(
cursorColor: cursorColor,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: GalleryLocalizations.of(context)
.demoTextFieldTellUsAboutYourself,
helperText: GalleryLocalizations.of(context)
.demoTextFieldKeepItShort,
labelText:
GalleryLocalizations.of(context).demoTextFieldLifeStory,
),
maxLines: 3,
),
sizedBoxSpace,
TextFormField(
cursorColor: cursorColor,
keyboardType: TextInputType.number,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText:
GalleryLocalizations.of(context).demoTextFieldSalary,
suffixText:
GalleryLocalizations.of(context).demoTextFieldUSD,
),
maxLines: 1,
),
sizedBoxSpace,
PasswordField(
fieldKey: _passwordFieldKey,
helperText:
GalleryLocalizations.of(context).demoTextFieldNoMoreThan,
labelText:
GalleryLocalizations.of(context).demoTextFieldPassword,
onFieldSubmitted: (value) {
setState(() {
person.password = value;
});
},
),
sizedBoxSpace,
TextFormField(
cursorColor: cursorColor,
decoration: InputDecoration(
filled: true,
labelText: GalleryLocalizations.of(context)
.demoTextFieldRetypePassword,
),
maxLength: 8,
obscureText: true,
validator: _validatePassword,
),
sizedBoxSpace,
Center(
child: RaisedButton(
child: Text(
GalleryLocalizations.of(context).demoTextFieldSubmit),
onPressed: _handleSubmitted,
),
),
sizedBoxSpace,
Text(
GalleryLocalizations.of(context).demoTextFieldRequiredField,
style: Theme.of(context).textTheme.caption,
),
sizedBoxSpace,
],
),
),
),
),
);
}
}
/// Format incoming numeric text to fit the format of (###) ###-#### ##
class _UsNumberTextInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final newTextLength = newValue.text.length;
final newText = StringBuffer();
int selectionIndex = newValue.selection.end;
int usedSubstringIndex = 0;
if (newTextLength >= 1) {
newText.write('(');
if (newValue.selection.end >= 1) selectionIndex++;
}
if (newTextLength >= 4) {
newText.write(newValue.text.substring(0, usedSubstringIndex = 3) + ') ');
if (newValue.selection.end >= 3) selectionIndex += 2;
}
if (newTextLength >= 7) {
newText.write(newValue.text.substring(3, usedSubstringIndex = 6) + '-');
if (newValue.selection.end >= 6) selectionIndex++;
}
if (newTextLength >= 11) {
newText.write(newValue.text.substring(6, usedSubstringIndex = 10) + ' ');
if (newValue.selection.end >= 10) selectionIndex++;
}
// Dump the rest.
if (newTextLength >= usedSubstringIndex) {
newText.write(newValue.text.substring(usedSubstringIndex));
}
return TextEditingValue(
text: newText.toString(),
selection: TextSelection.collapsed(offset: selectionIndex),
);
}
}
// END

View File

@@ -0,0 +1,46 @@
// Copyright 2019 The Flutter team. 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/material.dart';
import 'package:gallery/l10n/gallery_localizations.dart';
// BEGIN tooltipDemo
class TooltipDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(GalleryLocalizations.of(context).demoTooltipTitle),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
GalleryLocalizations.of(context).demoTooltipInstructions,
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Tooltip(
message:
GalleryLocalizations.of(context).starterAppTooltipSearch,
child: IconButton(
color: Theme.of(context).colorScheme.primary,
onPressed: () {},
icon: Icon(Icons.search),
),
),
],
),
),
),
);
}
}
// END