mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Adds CupertinoTheme (#23759)
This commit is contained in:
parent
8b34a12d45
commit
b8a035a3d4
|
@ -1 +1 @@
|
|||
e07cc0cb4fdf912062e71a6fd97cc91478d6e3b9
|
||||
c47f1308188dca65b3899228cac37f252ea8b411
|
||||
|
|
|
@ -13,8 +13,11 @@ class CupertinoProgressIndicatorDemo extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
previousPageTitle: 'Back',
|
||||
middle: const Text('Cupertino Activity Indicator'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
middle: const Text('Activity Indicator'),
|
||||
trailing: CupertinoDemoDocumentationButton(routeName),
|
||||
),
|
||||
child: const Center(
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../gallery/demo.dart';
|
||||
|
||||
|
@ -15,181 +14,193 @@ class CupertinoAlertDemo extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _CupertinoAlertDemoState extends State<CupertinoAlertDemo> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
String lastSelectedValue;
|
||||
|
||||
void showDemoDialog<T>({BuildContext context, Widget child}) {
|
||||
showDialog<T>(
|
||||
void showDemoDialog({BuildContext context, Widget child}) {
|
||||
showCupertinoDialog<String>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) => child,
|
||||
).then<void>((T value) {
|
||||
// The value passed to Navigator.pop() or null.
|
||||
).then((String value) {
|
||||
if (value != null) {
|
||||
_scaffoldKey.currentState.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('You selected: $value'),
|
||||
),
|
||||
);
|
||||
setState(() { lastSelectedValue = value; });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void showDemoActionSheet<T>({BuildContext context, Widget child}) {
|
||||
showCupertinoModalPopup<T>(
|
||||
void showDemoActionSheet({BuildContext context, Widget child}) {
|
||||
showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => child,
|
||||
).then<void>((T value) {
|
||||
).then((String value) {
|
||||
if (value != null) {
|
||||
_scaffoldKey.currentState.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('You selected: $value'),
|
||||
),
|
||||
);
|
||||
setState(() { lastSelectedValue = value; });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
appBar: AppBar(
|
||||
title: const Text('Cupertino Alerts'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoAlertDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Alerts'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoAlertDemo.routeName),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 72.0),
|
||||
children: <Widget>[
|
||||
CupertinoButton(
|
||||
child: const Text('Alert'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
onPressed: () {
|
||||
showDemoDialog<String>(
|
||||
context: context,
|
||||
child: CupertinoAlertDialog(
|
||||
title: const Text('Discard draft?'),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Discard'),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Discard');
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Cancel'),
|
||||
isDefaultAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cancel');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton(
|
||||
child: const Text('Alert with Title'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog<String>(
|
||||
context: context,
|
||||
child: CupertinoAlertDialog(
|
||||
title: const Text('Allow "Maps" to access your location while you are using the app?'),
|
||||
content: const Text('Your current location will be displayed on the map and used '
|
||||
'for directions, nearby search results, and estimated travel times.'),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Don\'t Allow'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Disallow');
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Allow'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Allow');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton(
|
||||
child: const Text('Alert with Buttons'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog<String>(
|
||||
context: context,
|
||||
child: const CupertinoDessertDialog(
|
||||
title: Text('Select Favorite Dessert'),
|
||||
content: Text('Please select your favorite type of dessert from the '
|
||||
'list below. Your selection will be used to customize the suggested '
|
||||
'list of eateries in your area.'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton(
|
||||
child: const Text('Alert Buttons Only'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog<String>(
|
||||
context: context,
|
||||
child: const CupertinoDessertDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton(
|
||||
child: const Text('Action Sheet'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoActionSheet<String>(
|
||||
context: context,
|
||||
child: CupertinoActionSheet(
|
||||
title: const Text('Favorite Dessert'),
|
||||
message: const Text('Please select the best dessert from the options below.'),
|
||||
actions: <Widget>[
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Profiteroles'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Profiteroles');
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Cannolis'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cannolis');
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Trifle'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Trifle');
|
||||
},
|
||||
),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: const Text('Cancel'),
|
||||
isDefaultAction: true,
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
final List<Widget> stackChildren = <Widget>[
|
||||
ListView(
|
||||
// Add more padding to the normal safe area.
|
||||
padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 72.0)
|
||||
+ MediaQuery.of(context).padding,
|
||||
children: <Widget>[
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Alert'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cancel');
|
||||
showDemoDialog(
|
||||
context: context,
|
||||
child: CupertinoAlertDialog(
|
||||
title: const Text('Discard draft?'),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Discard'),
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Discard');
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Cancel'),
|
||||
isDefaultAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cancel');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Alert with Title'),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog(
|
||||
context: context,
|
||||
child: CupertinoAlertDialog(
|
||||
title: const Text('Allow "Maps" to access your location while you are using the app?'),
|
||||
content: const Text('Your current location will be displayed on the map and used '
|
||||
'for directions, nearby search results, and estimated travel times.'),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Don\'t Allow'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Disallow');
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: const Text('Allow'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Allow');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Alert with Buttons'),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog(
|
||||
context: context,
|
||||
child: const CupertinoDessertDialog(
|
||||
title: Text('Select Favorite Dessert'),
|
||||
content: Text('Please select your favorite type of dessert from the '
|
||||
'list below. Your selection will be used to customize the suggested '
|
||||
'list of eateries in your area.'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Alert Buttons Only'),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoDialog(
|
||||
context: context,
|
||||
child: const CupertinoDessertDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(8.0)),
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Action Sheet'),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 36.0),
|
||||
onPressed: () {
|
||||
showDemoActionSheet(
|
||||
context: context,
|
||||
child: CupertinoActionSheet(
|
||||
title: const Text('Favorite Dessert'),
|
||||
message: const Text('Please select the best dessert from the options below.'),
|
||||
actions: <Widget>[
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Profiteroles'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Profiteroles');
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Cannolis'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cannolis');
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(
|
||||
child: const Text('Trifle'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Trifle');
|
||||
},
|
||||
),
|
||||
],
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
child: const Text('Cancel'),
|
||||
isDefaultAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, 'Cancel');
|
||||
},
|
||||
)
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
if (lastSelectedValue != null) {
|
||||
stackChildren.add(
|
||||
Positioned(
|
||||
bottom: 32.0,
|
||||
child: Text('You selected: $lastSelectedValue'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
}
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: stackChildren,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../gallery/demo.dart';
|
||||
|
||||
|
@ -19,64 +18,71 @@ class _CupertinoButtonDemoState extends State<CupertinoButtonsDemo> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Cupertino Buttons'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoButtonsDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Buttons'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoButtonsDemo.routeName),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget> [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'iOS themed buttons are flat. They can have borders or backgrounds but '
|
||||
'only when necessary.'
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget> [
|
||||
Text(_pressedCount > 0
|
||||
? 'Button pressed $_pressedCount time${_pressedCount == 1 ? "" : "s"}'
|
||||
: ' '),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
Align(
|
||||
alignment: const Alignment(0.0, -0.2),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
CupertinoButton(
|
||||
child: const Text('Cupertino Button'),
|
||||
onPressed: () {
|
||||
setState(() { _pressedCount += 1; });
|
||||
}
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'iOS themed buttons are flat. They can have borders or backgrounds but '
|
||||
'only when necessary.'
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget> [
|
||||
Text(_pressedCount > 0
|
||||
? 'Button pressed $_pressedCount time${_pressedCount == 1 ? "" : "s"}'
|
||||
: ' '),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
Align(
|
||||
alignment: const Alignment(0.0, -0.2),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
CupertinoButton(
|
||||
child: const Text('Cupertino Button'),
|
||||
onPressed: () {
|
||||
setState(() { _pressedCount += 1; });
|
||||
}
|
||||
),
|
||||
const CupertinoButton(
|
||||
child: Text('Disabled'),
|
||||
onPressed: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
const CupertinoButton(
|
||||
child: Text('Disabled'),
|
||||
onPressed: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
CupertinoButton.filled(
|
||||
child: const Text('With Background'),
|
||||
onPressed: () {
|
||||
setState(() { _pressedCount += 1; });
|
||||
}
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
const CupertinoButton.filled(
|
||||
child: Text('Disabled'),
|
||||
onPressed: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
CupertinoButton(
|
||||
child: const Text('With Background'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
onPressed: () {
|
||||
setState(() { _pressedCount += 1; });
|
||||
}
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(12.0)),
|
||||
const CupertinoButton(
|
||||
child: Text('Disabled'),
|
||||
color: CupertinoColors.activeBlue,
|
||||
onPressed: null,
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -52,11 +52,7 @@ class CupertinoNavigationDemo extends StatelessWidget {
|
|||
// Prevent swipe popping of this page. Use explicit exit buttons only.
|
||||
onWillPop: () => Future<bool>.value(true),
|
||||
child: DefaultTextStyle(
|
||||
style: const TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
fontSize: 17.0,
|
||||
color: CupertinoColors.black,
|
||||
),
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: CupertinoTabScaffold(
|
||||
tabBar: CupertinoTabBar(
|
||||
items: const <BottomNavigationBarItem>[
|
||||
|
@ -241,7 +237,6 @@ class Tab1RowItem extends StatelessWidget {
|
|||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.plus_circled,
|
||||
color: CupertinoColors.activeBlue,
|
||||
semanticLabel: 'Add',
|
||||
),
|
||||
onPressed: () { },
|
||||
|
@ -249,7 +244,6 @@ class Tab1RowItem extends StatelessWidget {
|
|||
CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: const Icon(CupertinoIcons.share,
|
||||
color: CupertinoColors.activeBlue,
|
||||
semanticLabel: 'Share',
|
||||
),
|
||||
onPressed: () { },
|
||||
|
@ -352,8 +346,7 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
CupertinoButton(
|
||||
color: CupertinoColors.activeBlue,
|
||||
CupertinoButton.filled(
|
||||
minSize: 30.0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
|
@ -367,12 +360,11 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
|
|||
),
|
||||
onPressed: () { },
|
||||
),
|
||||
CupertinoButton(
|
||||
color: CupertinoColors.activeBlue,
|
||||
CupertinoButton.filled(
|
||||
minSize: 30.0,
|
||||
padding: EdgeInsets.zero,
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
child: const Icon(CupertinoIcons.ellipsis, color: CupertinoColors.white),
|
||||
child: const Icon(CupertinoIcons.ellipsis),
|
||||
onPressed: () { },
|
||||
),
|
||||
],
|
||||
|
@ -722,7 +714,11 @@ class CupertinoDemoTab3 extends StatelessWidget {
|
|||
trailing: trailingButtons,
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(color: Color(0xFFEFEFF4)),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? CupertinoColors.extraLightBackgroundGray
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
const Padding(padding: EdgeInsets.only(top: 32.0)),
|
||||
|
@ -736,9 +732,9 @@ class CupertinoDemoTab3 extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
border: Border(
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
border: const Border(
|
||||
top: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
|
||||
bottom: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
|
||||
),
|
||||
|
@ -750,10 +746,10 @@ class CupertinoDemoTab3 extends StatelessWidget {
|
|||
top: false,
|
||||
bottom: false,
|
||||
child: Row(
|
||||
children: const <Widget>[
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Sign in',
|
||||
style: TextStyle(color: CupertinoColors.activeBlue),
|
||||
style: TextStyle(color: CupertinoTheme.of(context).primaryColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -791,8 +787,7 @@ class Tab3Dialog extends StatelessWidget {
|
|||
color: Color(0xFF646464),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 18.0)),
|
||||
CupertinoButton(
|
||||
color: CupertinoColors.activeBlue,
|
||||
CupertinoButton.filled(
|
||||
child: const Text('Sign in'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../gallery/demo.dart';
|
||||
|
@ -34,9 +33,9 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
|
|||
|
||||
Widget _buildMenu(List<Widget> children) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
border: Border(
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
border: const Border(
|
||||
top: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
|
||||
bottom: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
|
||||
),
|
||||
|
@ -47,16 +46,9 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
|
|||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: DefaultTextStyle(
|
||||
style: const TextStyle(
|
||||
letterSpacing: -0.24,
|
||||
fontSize: 17.0,
|
||||
color: CupertinoColors.black,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -249,19 +241,23 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Cupertino Picker'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoPickerDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Picker'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoPickerDemo.routeName),
|
||||
),
|
||||
body: DefaultTextStyle(
|
||||
style: const TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
fontSize: 17.0,
|
||||
color: CupertinoColors.black,
|
||||
),
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(color: Color(0xFFEFEFF4)),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? CupertinoColors.extraLightBackgroundGray
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
),
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
const Padding(padding: EdgeInsets.only(top: 32.0)),
|
||||
|
|
|
@ -39,19 +39,21 @@ class _CupertinoRefreshControlDemoState extends State<CupertinoRefreshControlDem
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTextStyle(
|
||||
style: const TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
inherit: false,
|
||||
fontSize: 17.0,
|
||||
color: CupertinoColors.black,
|
||||
),
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: CupertinoPageScaffold(
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(color: Color(0xFFEFEFF4)),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? CupertinoColors.extraLightBackgroundGray
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
),
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
CupertinoSliverNavigationBar(
|
||||
largeTitle: const Text('Cupertino Refresh'),
|
||||
largeTitle: const Text('Refresh'),
|
||||
// We're specifying a back label here because the previous page
|
||||
// is a Material page. CupertinoPageRoutes could auto-populate
|
||||
// these back labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoRefreshControlDemo.routeName),
|
||||
),
|
||||
|
@ -152,7 +154,7 @@ class _ListItem extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: CupertinoColors.white,
|
||||
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
height: 60.0,
|
||||
padding: const EdgeInsets.only(top: 9.0),
|
||||
child: Row(
|
||||
|
@ -215,11 +217,11 @@ class _ListItem extends StatelessWidget {
|
|||
letterSpacing: -0.41,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 9.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 9.0),
|
||||
child: Icon(
|
||||
CupertinoIcons.info,
|
||||
color: CupertinoColors.activeBlue
|
||||
color: CupertinoTheme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -50,68 +50,77 @@ class _CupertinoSegmentedControlDemoState extends State<CupertinoSegmentedContro
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Segmented Control'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoSegmentedControlDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Segmented Control'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoSegmentedControlDemo.routeName),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
),
|
||||
SizedBox(
|
||||
width: 500.0,
|
||||
child: CupertinoSegmentedControl<int>(
|
||||
children: children,
|
||||
onValueChanged: (int newValue) {
|
||||
setState(() {
|
||||
sharedValue = newValue;
|
||||
});
|
||||
},
|
||||
groupValue: sharedValue,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 32.0,
|
||||
horizontal: 16.0,
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 64.0,
|
||||
horizontal: 16.0,
|
||||
SizedBox(
|
||||
width: 500.0,
|
||||
child: CupertinoSegmentedControl<int>(
|
||||
children: children,
|
||||
onValueChanged: (int newValue) {
|
||||
setState(() {
|
||||
sharedValue = newValue;
|
||||
});
|
||||
},
|
||||
groupValue: sharedValue,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoColors.white,
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
boxShadow: const <BoxShadow>[
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 3.0),
|
||||
blurRadius: 5.0,
|
||||
spreadRadius: -1.0,
|
||||
color: _kKeyUmbraOpacity,
|
||||
),
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 6.0),
|
||||
blurRadius: 10.0,
|
||||
spreadRadius: 0.0,
|
||||
color: _kKeyPenumbraOpacity,
|
||||
),
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 1.0),
|
||||
blurRadius: 18.0,
|
||||
spreadRadius: 0.0,
|
||||
color: _kAmbientShadowOpacity,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: icons[sharedValue],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 32.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 64.0,
|
||||
horizontal: 16.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
boxShadow: const <BoxShadow>[
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 3.0),
|
||||
blurRadius: 5.0,
|
||||
spreadRadius: -1.0,
|
||||
color: _kKeyUmbraOpacity,
|
||||
),
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 6.0),
|
||||
blurRadius: 10.0,
|
||||
spreadRadius: 0.0,
|
||||
color: _kKeyPenumbraOpacity,
|
||||
),
|
||||
BoxShadow(
|
||||
offset: Offset(0.0, 1.0),
|
||||
blurRadius: 18.0,
|
||||
spreadRadius: 0.0,
|
||||
color: _kAmbientShadowOpacity,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: icons[sharedValue],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../gallery/demo.dart';
|
||||
|
||||
|
@ -20,49 +19,58 @@ class _CupertinoSliderDemoState extends State<CupertinoSliderDemo> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Cupertino Sliders'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoSliderDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Sliders'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoSliderDemo.routeName),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget> [
|
||||
CupertinoSlider(
|
||||
value: _value,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_value = value;
|
||||
});
|
||||
}
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget> [
|
||||
CupertinoSlider(
|
||||
value: _value,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_value = value;
|
||||
});
|
||||
}
|
||||
),
|
||||
Text('Cupertino Continuous: ${_value.toStringAsFixed(1)}'),
|
||||
]
|
||||
),
|
||||
Text('Cupertino Continuous: ${_value.toStringAsFixed(1)}'),
|
||||
]
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget> [
|
||||
CupertinoSlider(
|
||||
value: _discreteValue,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
divisions: 5,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_discreteValue = value;
|
||||
});
|
||||
}
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget> [
|
||||
CupertinoSlider(
|
||||
value: _discreteValue,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
divisions: 5,
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_discreteValue = value;
|
||||
});
|
||||
}
|
||||
),
|
||||
Text('Cupertino Discrete: $_discreteValue'),
|
||||
]
|
||||
),
|
||||
Text('Cupertino Discrete: $_discreteValue'),
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../gallery/demo.dart';
|
||||
|
||||
|
@ -20,62 +19,71 @@ class _CupertinoSwitchDemoState extends State<CupertinoSwitchDemo> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Cupertino Switch'),
|
||||
actions: <Widget>[MaterialDemoDocumentationButton(CupertinoSwitchDemo.routeName)],
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: const Text('Switch'),
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
trailing: CupertinoDemoDocumentationButton(CupertinoSwitchDemo.routeName),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: _switchValue,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
_switchValue = value;
|
||||
});
|
||||
},
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: _switchValue,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
_switchValue = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text(
|
||||
'Active'
|
||||
),
|
||||
],
|
||||
),
|
||||
const Text(
|
||||
'Active'
|
||||
),
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: const <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: true,
|
||||
onChanged: null,
|
||||
),
|
||||
Text(
|
||||
'Disabled'
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: const <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: false,
|
||||
onChanged: null,
|
||||
),
|
||||
Text(
|
||||
'Disabled'
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: const <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: true,
|
||||
onChanged: null,
|
||||
),
|
||||
Text(
|
||||
'Disabled'
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Semantics(
|
||||
container: true,
|
||||
child: Column(
|
||||
children: const <Widget>[
|
||||
CupertinoSwitch(
|
||||
value: false,
|
||||
onChanged: null,
|
||||
),
|
||||
Text(
|
||||
'Disabled'
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -160,6 +160,9 @@ class _CupertinoTextFieldDemoState extends State<CupertinoTextFieldDemo> {
|
|||
),
|
||||
child: CupertinoPageScaffold(
|
||||
navigationBar: const CupertinoNavigationBar(
|
||||
// We're specifying a back label here because the previous page is a
|
||||
// Material page. CupertinoPageRoutes could auto-populate these back
|
||||
// labels.
|
||||
previousPageTitle: 'Cupertino',
|
||||
middle: Text('Text Fields'),
|
||||
),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart' show defaultTargetPlatform;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart' show timeDilation;
|
||||
|
@ -125,7 +126,6 @@ class _GalleryAppState extends State<GalleryApp> {
|
|||
child: home,
|
||||
);
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
theme: _options.theme.data.copyWith(platform: _options.platform),
|
||||
title: 'Flutter Gallery',
|
||||
|
@ -137,7 +137,17 @@ class _GalleryAppState extends State<GalleryApp> {
|
|||
builder: (BuildContext context, Widget child) {
|
||||
return Directionality(
|
||||
textDirection: _options.textDirection,
|
||||
child: _applyTextScaleFactor(child),
|
||||
child: _applyTextScaleFactor(
|
||||
// Specifically use a blank Cupertino theme here and do not transfer
|
||||
// over the Material primary color etc except the brightness to
|
||||
// showcase standard iOS looks.
|
||||
CupertinoTheme(
|
||||
data: CupertinoThemeData(
|
||||
brightness: _options.theme.data.brightness,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
home: home,
|
||||
|
|
|
@ -30,5 +30,7 @@ export 'src/cupertino/tab_scaffold.dart';
|
|||
export 'src/cupertino/tab_view.dart';
|
||||
export 'src/cupertino/text_field.dart';
|
||||
export 'src/cupertino/text_selection.dart';
|
||||
export 'src/cupertino/text_theme.dart';
|
||||
export 'src/cupertino/theme.dart';
|
||||
export 'src/cupertino/thumb_painter.dart';
|
||||
export 'widgets.dart';
|
||||
|
|
|
@ -10,16 +10,7 @@ import 'colors.dart';
|
|||
import 'icons.dart';
|
||||
import 'localizations.dart';
|
||||
import 'route.dart';
|
||||
|
||||
// Based on specs from https://developer.apple.com/design/resources/ for
|
||||
// iOS 12.
|
||||
const TextStyle _kDefaultTextStyle = TextStyle(
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.38,
|
||||
color: CupertinoColors.black,
|
||||
decoration: TextDecoration.none,
|
||||
);
|
||||
import 'theme.dart';
|
||||
|
||||
/// An application that uses Cupertino design.
|
||||
///
|
||||
|
@ -79,6 +70,7 @@ class CupertinoApp extends StatefulWidget {
|
|||
Key key,
|
||||
this.navigatorKey,
|
||||
this.home,
|
||||
this.theme,
|
||||
this.routes = const <String, WidgetBuilder>{},
|
||||
this.initialRoute,
|
||||
this.onGenerateRoute,
|
||||
|
@ -114,6 +106,12 @@ class CupertinoApp extends StatefulWidget {
|
|||
/// {@macro flutter.widgets.widgetsApp.home}
|
||||
final Widget home;
|
||||
|
||||
/// The top-level [CupertinoTheme] styling.
|
||||
///
|
||||
/// A null [theme] or unspecified [theme] attributes will default to iOS
|
||||
/// system values.
|
||||
final CupertinoThemeData theme;
|
||||
|
||||
/// The application's top-level routing table.
|
||||
///
|
||||
/// When a named route is pushed with [Navigator.pushNamed], the route name is
|
||||
|
@ -266,48 +264,52 @@ class _CupertinoAppState extends State<CupertinoApp> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final CupertinoThemeData effectiveThemeData = widget.theme ?? const CupertinoThemeData();
|
||||
|
||||
return ScrollConfiguration(
|
||||
behavior: _AlwaysCupertinoScrollBehavior(),
|
||||
child: WidgetsApp(
|
||||
key: GlobalObjectKey(this),
|
||||
navigatorKey: widget.navigatorKey,
|
||||
navigatorObservers: _navigatorObservers,
|
||||
// TODO(dnfield): when https://github.com/dart-lang/sdk/issues/34572 is resolved
|
||||
// this can use type arguments again
|
||||
pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
|
||||
CupertinoPageRoute<dynamic>(settings: settings, builder: builder),
|
||||
home: widget.home,
|
||||
routes: widget.routes,
|
||||
initialRoute: widget.initialRoute,
|
||||
onGenerateRoute: widget.onGenerateRoute,
|
||||
onUnknownRoute: widget.onUnknownRoute,
|
||||
builder: widget.builder,
|
||||
title: widget.title,
|
||||
onGenerateTitle: widget.onGenerateTitle,
|
||||
textStyle: _kDefaultTextStyle,
|
||||
color: widget.color ?? CupertinoColors.activeBlue,
|
||||
locale: widget.locale,
|
||||
localizationsDelegates: _localizationsDelegates,
|
||||
localeResolutionCallback: widget.localeResolutionCallback,
|
||||
localeListResolutionCallback: widget.localeListResolutionCallback,
|
||||
supportedLocales: widget.supportedLocales,
|
||||
showPerformanceOverlay: widget.showPerformanceOverlay,
|
||||
checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
|
||||
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
|
||||
showSemanticsDebugger: widget.showSemanticsDebugger,
|
||||
debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
|
||||
inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) {
|
||||
return CupertinoButton(
|
||||
child: const Icon(
|
||||
CupertinoIcons.search,
|
||||
size: 28.0,
|
||||
color: CupertinoColors.white,
|
||||
),
|
||||
color: CupertinoColors.activeBlue,
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
},
|
||||
child: CupertinoTheme(
|
||||
data: effectiveThemeData,
|
||||
child: WidgetsApp(
|
||||
key: GlobalObjectKey(this),
|
||||
navigatorKey: widget.navigatorKey,
|
||||
navigatorObservers: _navigatorObservers,
|
||||
// TODO(dnfield): when https://github.com/dart-lang/sdk/issues/34572 is resolved
|
||||
// this can use type arguments again
|
||||
pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
|
||||
CupertinoPageRoute<dynamic>(settings: settings, builder: builder),
|
||||
home: widget.home,
|
||||
routes: widget.routes,
|
||||
initialRoute: widget.initialRoute,
|
||||
onGenerateRoute: widget.onGenerateRoute,
|
||||
onUnknownRoute: widget.onUnknownRoute,
|
||||
builder: widget.builder,
|
||||
title: widget.title,
|
||||
onGenerateTitle: widget.onGenerateTitle,
|
||||
textStyle: effectiveThemeData.textTheme.textStyle,
|
||||
color: widget.color ?? CupertinoColors.activeBlue,
|
||||
locale: widget.locale,
|
||||
localizationsDelegates: _localizationsDelegates,
|
||||
localeResolutionCallback: widget.localeResolutionCallback,
|
||||
localeListResolutionCallback: widget.localeListResolutionCallback,
|
||||
supportedLocales: widget.supportedLocales,
|
||||
showPerformanceOverlay: widget.showPerformanceOverlay,
|
||||
checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
|
||||
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
|
||||
showSemanticsDebugger: widget.showSemanticsDebugger,
|
||||
debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
|
||||
inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) {
|
||||
return CupertinoButton.filled(
|
||||
child: const Icon(
|
||||
CupertinoIcons.search,
|
||||
size: 28.0,
|
||||
color: CupertinoColors.white,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import 'dart:ui' show ImageFilter;
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Standard iOS 10 tab bar height.
|
||||
const double _kTabBarHeight = 50.0;
|
||||
|
||||
const Color _kDefaultTabBarBackgroundColor = Color(0xCCF8F8F8);
|
||||
const Color _kDefaultTabBarBorderColor = Color(0x4C000000);
|
||||
|
||||
/// An iOS-styled bottom navigation tab bar.
|
||||
|
@ -21,10 +21,12 @@ const Color _kDefaultTabBarBorderColor = Color(0x4C000000);
|
|||
///
|
||||
/// This [StatelessWidget] doesn't store the active tab itself. You must
|
||||
/// listen to the [onTap] callbacks and call `setState` with a new [currentIndex]
|
||||
/// for the new selection to reflect.
|
||||
/// for the new selection to reflect. This can also be done automatically
|
||||
/// by wrapping this with a [CupertinoTabScaffold].
|
||||
///
|
||||
/// Tab changes typically trigger a switch between [Navigator]s, each with its
|
||||
/// own navigation stack, per standard iOS design.
|
||||
/// own navigation stack, per standard iOS design. This can be done by using
|
||||
/// [CupertinoTabView]s inside each tab builder in [CupertinoTabScaffold].
|
||||
///
|
||||
/// If the given [backgroundColor]'s opacity is not 1.0 (which is the case by
|
||||
/// default), it will produce a blurring effect to the content behind it.
|
||||
|
@ -40,8 +42,8 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
@required this.items,
|
||||
this.onTap,
|
||||
this.currentIndex = 0,
|
||||
this.backgroundColor = _kDefaultTabBarBackgroundColor,
|
||||
this.activeColor = CupertinoColors.activeBlue,
|
||||
this.backgroundColor,
|
||||
this.activeColor,
|
||||
this.inactiveColor = CupertinoColors.inactiveGray,
|
||||
this.iconSize = 30.0,
|
||||
this.border = const Border(
|
||||
|
@ -56,6 +58,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
assert(currentIndex != null),
|
||||
assert(0 <= currentIndex && currentIndex < items.length),
|
||||
assert(iconSize != null),
|
||||
assert(inactiveColor != null),
|
||||
super(key: key);
|
||||
|
||||
/// The interactive items laid out within the bottom navigation bar.
|
||||
|
@ -78,14 +81,20 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
/// The background color of the tab bar. If it contains transparency, the
|
||||
/// tab bar will automatically produce a blurring effect to the content
|
||||
/// behind it.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `barBackgroundColor` when null.
|
||||
final Color backgroundColor;
|
||||
|
||||
/// The foreground color of the icon and title for the [BottomNavigationBarItem]
|
||||
/// of the selected tab.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color activeColor;
|
||||
|
||||
/// The foreground color of the icon and title for the [BottomNavigationBarItem]s
|
||||
/// in the unselected state.
|
||||
///
|
||||
/// Defaults to [CupertinoColors.inactiveGray] and cannot be null.
|
||||
final Color inactiveColor;
|
||||
|
||||
/// The size of all of the [BottomNavigationBarItem] icons.
|
||||
|
@ -102,19 +111,25 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
/// The default value is a one physical pixel top border with grey color.
|
||||
final Border border;
|
||||
|
||||
/// True if the tab bar's background color has no transparency.
|
||||
bool get opaque => backgroundColor.alpha == 0xFF;
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(_kTabBarHeight);
|
||||
|
||||
/// Indicates whether the tab bar is fully opaque or can have contents behind
|
||||
/// it show through it.
|
||||
bool opaque(BuildContext context) {
|
||||
final Color backgroundColor =
|
||||
this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor;
|
||||
return backgroundColor.alpha == 0xFF;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
|
||||
Widget result = DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: border,
|
||||
color: backgroundColor,
|
||||
color: backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: _kTabBarHeight + bottomPadding,
|
||||
|
@ -124,19 +139,13 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
size: iconSize,
|
||||
),
|
||||
child: DefaultTextStyle( // Default with the inactive state.
|
||||
style: TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
fontSize: 10.0,
|
||||
letterSpacing: 0.1,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: inactiveColor,
|
||||
),
|
||||
style: CupertinoTheme.of(context).textTheme.tabLabelTextStyle.copyWith(color: inactiveColor),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: bottomPadding),
|
||||
child: Row(
|
||||
// Align bottom since we want the labels to be aligned.
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: _buildTabItems(),
|
||||
children: _buildTabItems(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -144,7 +153,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
),
|
||||
);
|
||||
|
||||
if (!opaque) {
|
||||
if (!opaque(context)) {
|
||||
// For non-opaque backgrounds, apply a blur effect.
|
||||
result = ClipRect(
|
||||
child: BackdropFilter(
|
||||
|
@ -157,13 +166,14 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
return result;
|
||||
}
|
||||
|
||||
List<Widget> _buildTabItems() {
|
||||
List<Widget> _buildTabItems(BuildContext context) {
|
||||
final List<Widget> result = <Widget>[];
|
||||
|
||||
for (int index = 0; index < items.length; index += 1) {
|
||||
final bool active = index == currentIndex;
|
||||
result.add(
|
||||
_wrapActiveItem(
|
||||
context,
|
||||
Expanded(
|
||||
child: Semantics(
|
||||
selected: active,
|
||||
|
@ -205,10 +215,11 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
}
|
||||
|
||||
/// Change the active tab item's icon and title colors to active.
|
||||
Widget _wrapActiveItem(Widget item, { @required bool active }) {
|
||||
Widget _wrapActiveItem(BuildContext context, Widget item, { @required bool active }) {
|
||||
if (!active)
|
||||
return item;
|
||||
|
||||
final Color activeColor = this.activeColor ?? CupertinoTheme.of(context).primaryColor;
|
||||
return IconTheme.merge(
|
||||
data: IconThemeData(color: activeColor),
|
||||
child: DefaultTextStyle.merge(
|
||||
|
|
|
@ -5,28 +5,11 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
const Color _kDisabledBackground = Color(0xFFA9A9A9);
|
||||
const Color _kDisabledForeground = Color(0xFFC4C4C4);
|
||||
|
||||
const TextStyle _kButtonTextStyle = TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
inherit: false,
|
||||
fontSize: 17.5,
|
||||
letterSpacing: -0.24,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: CupertinoColors.activeBlue,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
);
|
||||
|
||||
final TextStyle _kDisabledButtonTextStyle = _kButtonTextStyle.copyWith(
|
||||
color: _kDisabledForeground,
|
||||
);
|
||||
|
||||
final TextStyle _kBackgroundButtonTextStyle = _kButtonTextStyle.copyWith(
|
||||
color: CupertinoColors.white,
|
||||
);
|
||||
// Measured against iOS 12 in Xcode.
|
||||
const Color _kDisabledForeground = Color(0xFFD1D1D1);
|
||||
|
||||
const EdgeInsets _kButtonPadding = EdgeInsets.all(16.0);
|
||||
const EdgeInsets _kBackgroundButtonPadding = EdgeInsets.symmetric(
|
||||
|
@ -53,7 +36,26 @@ class CupertinoButton extends StatefulWidget {
|
|||
this.pressedOpacity = 0.1,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(8.0)),
|
||||
@required this.onPressed,
|
||||
}) : assert(pressedOpacity == null || (pressedOpacity >= 0.0 && pressedOpacity <= 1.0));
|
||||
}) : assert(pressedOpacity == null || (pressedOpacity >= 0.0 && pressedOpacity <= 1.0)),
|
||||
_filled = false;
|
||||
|
||||
/// Creates an iOS-style button with a filled background.
|
||||
///
|
||||
/// The background color is derived from the [CupertinoTheme]'s `primaryColor`.
|
||||
///
|
||||
/// To specify a custom background color, use the [color] argument of the
|
||||
/// default constructor.
|
||||
const CupertinoButton.filled({
|
||||
@required this.child,
|
||||
this.padding,
|
||||
this.disabledColor,
|
||||
this.minSize = 44.0,
|
||||
this.pressedOpacity = 0.1,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(8.0)),
|
||||
@required this.onPressed,
|
||||
}) : assert(pressedOpacity == null || (pressedOpacity >= 0.0 && pressedOpacity <= 1.0)),
|
||||
color = null,
|
||||
_filled = true;
|
||||
|
||||
/// The widget below this widget in the tree.
|
||||
///
|
||||
|
@ -68,6 +70,9 @@ class CupertinoButton extends StatefulWidget {
|
|||
/// The color of the button's background.
|
||||
///
|
||||
/// Defaults to null which produces a button with no background or border.
|
||||
///
|
||||
/// Defaults to the [CupertinoTheme]'s `primaryColor` when the
|
||||
/// [CupertinoButton.filled] constructor is used.
|
||||
final Color color;
|
||||
|
||||
/// The color of the button's background when the button is disabled.
|
||||
|
@ -105,6 +110,8 @@ class CupertinoButton extends StatefulWidget {
|
|||
/// Defaults to round corners of 8 logical pixels.
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
final bool _filled;
|
||||
|
||||
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
|
||||
/// enable a button, set its [onPressed] property to a non-null value.
|
||||
bool get enabled => onPressed != null;
|
||||
|
@ -198,7 +205,15 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool enabled = widget.enabled;
|
||||
final Color backgroundColor = widget.color;
|
||||
final Color primaryColor = CupertinoTheme.of(context).primaryColor;
|
||||
final Color backgroundColor = widget.color ?? (widget._filled ? primaryColor : null);
|
||||
final Color foregroundColor = backgroundColor != null
|
||||
? CupertinoTheme.of(context).primaryContrastingColor
|
||||
: enabled
|
||||
? primaryColor
|
||||
: _kDisabledForeground;
|
||||
final TextStyle textStyle =
|
||||
CupertinoTheme.of(context).textTheme.textStyle.copyWith(color: foregroundColor);
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
|
@ -232,12 +247,11 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
|||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: DefaultTextStyle(
|
||||
style: backgroundColor != null
|
||||
? _kBackgroundButtonTextStyle
|
||||
: enabled
|
||||
? _kButtonTextStyle
|
||||
: _kDisabledButtonTextStyle,
|
||||
child: widget.child,
|
||||
style: textStyle,
|
||||
child: IconTheme(
|
||||
data: IconThemeData(color: foregroundColor),
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -11,15 +11,27 @@ class CupertinoColors {
|
|||
|
||||
/// iOS 10's default blue color. Used to indicate active elements such as
|
||||
/// buttons, selected tabs and your own chat bubbles.
|
||||
///
|
||||
/// This is SystemBlue in the iOS palette.
|
||||
static const Color activeBlue = Color(0xFF007AFF);
|
||||
|
||||
/// iOS 10's default green color. Used to indicate active accents such as
|
||||
/// the switch in its on state and some accent buttons such as the call button
|
||||
/// and Apple Map's 'Go' button.
|
||||
///
|
||||
/// This is SystemGreen in the iOS palette.
|
||||
static const Color activeGreen = Color(0xFF4CD964);
|
||||
|
||||
/// iOS 12's default dark mode color. Used in place of the [activeBlue] color
|
||||
/// as the default active elements' color when the theme's brightness is dark.
|
||||
///
|
||||
/// This is SystemOrange in the iOS palette.
|
||||
static const Color activeOrange = Color(0xFFFF9500);
|
||||
|
||||
/// Opaque white color. Used for backgrounds and fonts against dark backgrounds.
|
||||
///
|
||||
/// This is SystemWhiteColor in the iOS palette.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [material.Colors.white], the same color, in the material design palette.
|
||||
|
@ -28,6 +40,8 @@ class CupertinoColors {
|
|||
|
||||
/// Opaque black color. Used for texts against light backgrounds.
|
||||
///
|
||||
/// This is SystemBlackColor in the iOS palette.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [material.Colors.black], the same color, in the material design palette.
|
||||
|
@ -35,12 +49,26 @@ class CupertinoColors {
|
|||
static const Color black = Color(0xFF000000);
|
||||
|
||||
/// Used in iOS 10 for light background fills such as the chat bubble background.
|
||||
///
|
||||
/// This is SystemLightGrayColor in the iOS palette.
|
||||
static const Color lightBackgroundGray = Color(0xFFE5E5EA);
|
||||
|
||||
/// Used in iOS 12 for very light background fills in tables between cell groups.
|
||||
///
|
||||
/// This is SystemExtraLightGrayColor in the iOS palette.
|
||||
static const Color extraLightBackgroundGray = Color(0xFFEFEFF4);
|
||||
|
||||
/// Used in iOS 12 for very dark background fills in tables between cell groups
|
||||
/// in dark mode.
|
||||
// Value derived from screenshot from the dark themed Apple Watch app.
|
||||
static const Color darkBackgroundGray = Color(0xFF171717);
|
||||
|
||||
/// Used in iOS 11 for unselected selectables such as tab bar items in their
|
||||
/// inactive state or de-emphasized subtitles and details text.
|
||||
///
|
||||
/// Not the same gray as disabled buttons etc.
|
||||
///
|
||||
/// This is SystemGrayColor in the iOS palette.
|
||||
static const Color inactiveGray = Color(0xFF8E8E93);
|
||||
|
||||
/// Used for iOS 10 for destructive actions such as the delete actions in
|
||||
|
@ -48,5 +76,7 @@ class CupertinoColors {
|
|||
///
|
||||
/// Not the same red as the camera shutter or springboard icon notifications
|
||||
/// or the foreground red theme in various native apps such as HealthKit.
|
||||
///
|
||||
/// This is SystemRed in the iOS palette.
|
||||
static const Color destructiveRed = Color(0xFFFF3B30);
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'button.dart';
|
||||
import 'colors.dart';
|
||||
import 'icons.dart';
|
||||
import 'page_scaffold.dart';
|
||||
import 'route.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
/// Standard iOS navigation bar height without the status bar.
|
||||
///
|
||||
|
@ -36,7 +36,6 @@ const double _kNavBarBackButtonTapWidth = 50.0;
|
|||
/// Title text transfer fade.
|
||||
const Duration _kNavBarTitleFadeDuration = Duration(milliseconds: 150);
|
||||
|
||||
const Color _kDefaultNavBarBackgroundColor = Color(0xCCF8F8F8);
|
||||
const Color _kDefaultNavBarBorderColor = Color(0x4C000000);
|
||||
|
||||
const Border _kDefaultNavBarBorder = Border(
|
||||
|
@ -47,22 +46,6 @@ const Border _kDefaultNavBarBorder = Border(
|
|||
),
|
||||
);
|
||||
|
||||
const TextStyle _kMiddleTitleTextStyle = TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
fontSize: 17.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.08,
|
||||
color: CupertinoColors.black,
|
||||
);
|
||||
|
||||
const TextStyle _kLargeTitleTextStyle = TextStyle(
|
||||
fontFamily: '.SF Pro Display',
|
||||
fontSize: 34.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.24,
|
||||
color: CupertinoColors.black,
|
||||
);
|
||||
|
||||
// There's a single tag for all instances of navigation bars because they can
|
||||
// all transition between each other (per Navigator) via Hero transitions.
|
||||
const _HeroTag _defaultHeroTag = _HeroTag(null);
|
||||
|
@ -94,15 +77,6 @@ class _HeroTag {
|
|||
}
|
||||
}
|
||||
|
||||
TextStyle _navBarItemStyle(Color color) {
|
||||
return TextStyle(
|
||||
fontFamily: '.SF UI Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.24,
|
||||
color: color,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns `child` wrapped with background and a bottom border if background color
|
||||
/// is opaque. Otherwise, also blur with [BackdropFilter].
|
||||
///
|
||||
|
@ -145,6 +119,21 @@ Widget _wrapWithBackground({
|
|||
);
|
||||
}
|
||||
|
||||
// This exists to support backward compatibility with arguments like
|
||||
// `actionsForegroundColor`. CupertinoThemes can be used to support these
|
||||
// scenarios now. To support `actionsForegroundColor`, the nav bar rewraps
|
||||
// its children with a CupertinoTheme.
|
||||
Widget _wrapActiveColor(Color color, BuildContext context, Widget child) {
|
||||
if (color == null) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return CupertinoTheme(
|
||||
data: CupertinoTheme.of(context).copyWith(primaryColor: color),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
// Whether the current route supports nav bar hero transitions from or to.
|
||||
bool _isTransitionable(BuildContext context) {
|
||||
final ModalRoute<dynamic> route = ModalRoute.of(context);
|
||||
|
@ -206,9 +195,9 @@ class CupertinoNavigationBar extends StatefulWidget implements ObstructingPrefer
|
|||
this.middle,
|
||||
this.trailing,
|
||||
this.border = _kDefaultNavBarBorder,
|
||||
this.backgroundColor = _kDefaultNavBarBackgroundColor,
|
||||
this.backgroundColor,
|
||||
this.padding,
|
||||
this.actionsForegroundColor = CupertinoColors.activeBlue,
|
||||
this.actionsForegroundColor,
|
||||
this.transitionBetweenRoutes = true,
|
||||
this.heroTag = _defaultHeroTag,
|
||||
}) : assert(automaticallyImplyLeading != null),
|
||||
|
@ -296,6 +285,8 @@ class CupertinoNavigationBar extends StatefulWidget implements ObstructingPrefer
|
|||
/// The background color of the navigation bar. If it contains transparency, the
|
||||
/// tab bar will automatically produce a blurring effect to the content
|
||||
/// behind it.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `barBackgroundColor` if null.
|
||||
/// {@endtemplate}
|
||||
final Color backgroundColor;
|
||||
|
||||
|
@ -321,11 +312,16 @@ class CupertinoNavigationBar extends StatefulWidget implements ObstructingPrefer
|
|||
/// {@endtemplate}
|
||||
final Border border;
|
||||
|
||||
/// {@template flutter.cupertino.navBar.actionsForegroundColor}
|
||||
/// Default color used for text and icons of the [leading] and [trailing]
|
||||
/// widgets in the navigation bar.
|
||||
///
|
||||
/// Defaults to the `primaryColor` of the [CupertinoTheme] when null.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// The default color for text in the [middle] slot is always black, as per
|
||||
/// iOS standard design.
|
||||
@Deprecated('Use CupertinoTheme and primaryColor to propagate color')
|
||||
final Color actionsForegroundColor;
|
||||
|
||||
/// {@template flutter.cupertino.navBar.transitionBetweenRoutes}
|
||||
|
@ -366,7 +362,7 @@ class CupertinoNavigationBar extends StatefulWidget implements ObstructingPrefer
|
|||
|
||||
/// True if the navigation bar's background color has no transparency.
|
||||
@override
|
||||
bool get fullObstruction => backgroundColor.alpha == 0xFF;
|
||||
bool get fullObstruction => backgroundColor == null ? null : backgroundColor.alpha == 0xFF;
|
||||
|
||||
@override
|
||||
Size get preferredSize {
|
||||
|
@ -393,6 +389,9 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color backgroundColor =
|
||||
widget.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor;
|
||||
|
||||
final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
|
||||
keys: keys,
|
||||
route: ModalRoute.of(context),
|
||||
|
@ -403,40 +402,55 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
|
|||
userMiddle: widget.middle,
|
||||
userTrailing: widget.trailing,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: widget.actionsForegroundColor,
|
||||
userLargeTitle: null,
|
||||
large: false,
|
||||
);
|
||||
|
||||
final Widget navBar = _wrapWithBackground(
|
||||
border: widget.border,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
child: _PersistentNavigationBar(
|
||||
components: components,
|
||||
padding: widget.padding,
|
||||
backgroundColor: backgroundColor,
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: _PersistentNavigationBar(
|
||||
components: components,
|
||||
padding: widget.padding,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (!widget.transitionBetweenRoutes || !_isTransitionable(context)) {
|
||||
return navBar;
|
||||
// Lint ignore to maintain backward compatibility.
|
||||
return _wrapActiveColor(widget.actionsForegroundColor, context, navBar); // ignore: deprecated_member_use
|
||||
}
|
||||
|
||||
return Hero(
|
||||
tag: widget.heroTag == _defaultHeroTag
|
||||
? _HeroTag(Navigator.of(context))
|
||||
: widget.heroTag,
|
||||
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
||||
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
||||
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
||||
transitionOnUserGestures: true,
|
||||
child: _TransitionableNavigationBar(
|
||||
componentsKeys: keys,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
actionsForegroundColor: widget.actionsForegroundColor,
|
||||
border: widget.border,
|
||||
hasUserMiddle: widget.middle != null,
|
||||
largeExpanded: false,
|
||||
child: navBar,
|
||||
return _wrapActiveColor(
|
||||
// Lint ignore to maintain backward compatibility.
|
||||
widget.actionsForegroundColor, // ignore: deprecated_member_use
|
||||
context,
|
||||
Builder(
|
||||
// Get the context that might have a possibly changed CupertinoTheme.
|
||||
builder: (BuildContext context) {
|
||||
return Hero(
|
||||
tag: widget.heroTag == _defaultHeroTag
|
||||
? _HeroTag(Navigator.of(context))
|
||||
: widget.heroTag,
|
||||
createRectTween: _linearTranslateWithLargestRectSizeTween,
|
||||
placeholderBuilder: _navBarHeroLaunchPadBuilder,
|
||||
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
|
||||
transitionOnUserGestures: true,
|
||||
child: _TransitionableNavigationBar(
|
||||
componentsKeys: keys,
|
||||
backgroundColor: backgroundColor,
|
||||
backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
||||
titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
largeTitleTextStyle: null,
|
||||
border: widget.border,
|
||||
hasUserMiddle: widget.middle != null,
|
||||
largeExpanded: false,
|
||||
child: navBar,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -502,9 +516,9 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
|
|||
this.middle,
|
||||
this.trailing,
|
||||
this.border = _kDefaultNavBarBorder,
|
||||
this.backgroundColor = _kDefaultNavBarBackgroundColor,
|
||||
this.backgroundColor,
|
||||
this.padding,
|
||||
this.actionsForegroundColor = CupertinoColors.activeBlue,
|
||||
this.actionsForegroundColor,
|
||||
this.transitionBetweenRoutes = true,
|
||||
this.heroTag = _defaultHeroTag,
|
||||
}) : assert(automaticallyImplyLeading != null),
|
||||
|
@ -582,11 +596,11 @@ class CupertinoSliverNavigationBar extends StatefulWidget {
|
|||
/// {@macro flutter.cupertino.navBar.border}
|
||||
final Border border;
|
||||
|
||||
/// Default color used for text and icons of the [leading] and [trailing]
|
||||
/// widgets in the navigation bar.
|
||||
/// {@macro flutter.cupertino.navBar.actionsForegroundColor}
|
||||
///
|
||||
/// The default color for text in the [largeTitle] slot is always black, as per
|
||||
/// iOS standard design.
|
||||
@Deprecated('Use CupertinoTheme and primaryColor to propagate color')
|
||||
final Color actionsForegroundColor;
|
||||
|
||||
/// {@macro flutter.cupertino.navBar.transitionBetweenRoutes}
|
||||
|
@ -616,6 +630,9 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Lint ignore to maintain backward compatibility.
|
||||
final Color actionsForegroundColor = widget.actionsForegroundColor ?? CupertinoTheme.of(context).primaryColor; // ignore: deprecated_member_use
|
||||
|
||||
final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
|
||||
keys: keys,
|
||||
route: ModalRoute.of(context),
|
||||
|
@ -627,24 +644,28 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
|
|||
userTrailing: widget.trailing,
|
||||
userLargeTitle: widget.largeTitle,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: widget.actionsForegroundColor,
|
||||
large: true,
|
||||
);
|
||||
|
||||
return SliverPersistentHeader(
|
||||
pinned: true, // iOS navigation bars are always pinned.
|
||||
delegate: _LargeTitleNavigationBarSliverDelegate(
|
||||
keys: keys,
|
||||
components: components,
|
||||
userMiddle: widget.middle,
|
||||
backgroundColor: widget.backgroundColor,
|
||||
border: widget.border,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: widget.actionsForegroundColor,
|
||||
transitionBetweenRoutes: widget.transitionBetweenRoutes,
|
||||
heroTag: widget.heroTag,
|
||||
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
|
||||
alwaysShowMiddle: widget.middle != null,
|
||||
return _wrapActiveColor(
|
||||
// Lint ignore to maintain backward compatibility.
|
||||
widget.actionsForegroundColor, // ignore: deprecated_member_use
|
||||
context,
|
||||
SliverPersistentHeader(
|
||||
pinned: true, // iOS navigation bars are always pinned.
|
||||
delegate: _LargeTitleNavigationBarSliverDelegate(
|
||||
keys: keys,
|
||||
components: components,
|
||||
userMiddle: widget.middle,
|
||||
backgroundColor: widget.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor,
|
||||
border: widget.border,
|
||||
padding: widget.padding,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
transitionBetweenRoutes: widget.transitionBetweenRoutes,
|
||||
heroTag: widget.heroTag,
|
||||
persistentHeight: _kNavBarPersistentHeight + MediaQuery.of(context).padding.top,
|
||||
alwaysShowMiddle: widget.middle != null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -702,40 +723,43 @@ class _LargeTitleNavigationBarSliverDelegate
|
|||
final Widget navBar = _wrapWithBackground(
|
||||
border: border,
|
||||
backgroundColor: backgroundColor,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
top: persistentHeight,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
child: ClipRect(
|
||||
// The large title starts at the persistent bar.
|
||||
// It's aligned with the bottom of the sliver and expands clipped
|
||||
// and behind the persistent bar.
|
||||
child: OverflowBox(
|
||||
minHeight: 0.0,
|
||||
maxHeight: double.infinity,
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
start: _kNavBarEdgePadding,
|
||||
bottom: 8.0, // Bottom has a different padding.
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: AnimatedOpacity(
|
||||
opacity: showLargeTitle ? 1.0 : 0.0,
|
||||
duration: _kNavBarTitleFadeDuration,
|
||||
child: Semantics(
|
||||
header: true,
|
||||
child: DefaultTextStyle(
|
||||
style: _kLargeTitleTextStyle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: components.largeTitle,
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
top: persistentHeight,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
child: ClipRect(
|
||||
// The large title starts at the persistent bar.
|
||||
// It's aligned with the bottom of the sliver and expands clipped
|
||||
// and behind the persistent bar.
|
||||
child: OverflowBox(
|
||||
minHeight: 0.0,
|
||||
maxHeight: double.infinity,
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
start: _kNavBarEdgePadding,
|
||||
bottom: 8.0, // Bottom has a different padding.
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: AnimatedOpacity(
|
||||
opacity: showLargeTitle ? 1.0 : 0.0,
|
||||
duration: _kNavBarTitleFadeDuration,
|
||||
child: Semantics(
|
||||
header: true,
|
||||
child: DefaultTextStyle(
|
||||
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: components.largeTitle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -743,14 +767,14 @@ class _LargeTitleNavigationBarSliverDelegate
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
top: 0.0,
|
||||
child: persistentNavigationBar,
|
||||
),
|
||||
],
|
||||
Positioned(
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
top: 0.0,
|
||||
child: persistentNavigationBar,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -772,7 +796,9 @@ class _LargeTitleNavigationBarSliverDelegate
|
|||
child: _TransitionableNavigationBar(
|
||||
componentsKeys: keys,
|
||||
backgroundColor: backgroundColor,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
|
||||
titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
largeTitleTextStyle: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
||||
border: border,
|
||||
hasUserMiddle: userMiddle != null,
|
||||
largeExpanded: showLargeTitle,
|
||||
|
@ -822,7 +848,7 @@ class _PersistentNavigationBar extends StatelessWidget {
|
|||
|
||||
if (middle != null) {
|
||||
middle = DefaultTextStyle(
|
||||
style: _kMiddleTitleTextStyle,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
child: Semantics(header: true, child: middle),
|
||||
);
|
||||
// When the middle's visibility can change on the fly like with large title
|
||||
|
@ -844,7 +870,6 @@ class _PersistentNavigationBar extends StatelessWidget {
|
|||
leading = CupertinoNavigationBarBackButton._assemble(
|
||||
backChevron,
|
||||
backLabel,
|
||||
components.actionsForegroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -919,7 +944,6 @@ class _NavigationBarStaticComponents {
|
|||
@required Widget userTrailing,
|
||||
@required Widget userLargeTitle,
|
||||
@required EdgeInsetsDirectional padding,
|
||||
@required this.actionsForegroundColor,
|
||||
@required bool large,
|
||||
}) : leading = createLeading(
|
||||
leadingKey: keys.leadingKey,
|
||||
|
@ -927,7 +951,6 @@ class _NavigationBarStaticComponents {
|
|||
route: route,
|
||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||
padding: padding,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
),
|
||||
backChevron = createBackChevron(
|
||||
backChevronKey: keys.backChevronKey,
|
||||
|
@ -954,7 +977,6 @@ class _NavigationBarStaticComponents {
|
|||
trailingKey: keys.trailingKey,
|
||||
userTrailing: userTrailing,
|
||||
padding: padding,
|
||||
actionsForegroundColor: actionsForegroundColor,
|
||||
),
|
||||
largeTitle = createLargeTitle(
|
||||
largeTitleKey: keys.largeTitleKey,
|
||||
|
@ -978,8 +1000,6 @@ class _NavigationBarStaticComponents {
|
|||
return null;
|
||||
}
|
||||
|
||||
final Color actionsForegroundColor;
|
||||
|
||||
final KeyedSubtree leading;
|
||||
static KeyedSubtree createLeading({
|
||||
@required GlobalKey leadingKey,
|
||||
|
@ -987,7 +1007,6 @@ class _NavigationBarStaticComponents {
|
|||
@required ModalRoute<dynamic> route,
|
||||
@required bool automaticallyImplyLeading,
|
||||
@required EdgeInsetsDirectional padding,
|
||||
@required Color actionsForegroundColor
|
||||
}) {
|
||||
Widget leadingContent;
|
||||
|
||||
|
@ -1016,15 +1035,11 @@ class _NavigationBarStaticComponents {
|
|||
padding: EdgeInsetsDirectional.only(
|
||||
start: padding?.start ?? _kNavBarEdgePadding,
|
||||
),
|
||||
child: DefaultTextStyle(
|
||||
style: _navBarItemStyle(actionsForegroundColor),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(
|
||||
color: actionsForegroundColor,
|
||||
size: 32.0,
|
||||
),
|
||||
child: leadingContent,
|
||||
child: IconTheme.merge(
|
||||
data: const IconThemeData(
|
||||
size: 32.0,
|
||||
),
|
||||
child: leadingContent,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1116,7 +1131,6 @@ class _NavigationBarStaticComponents {
|
|||
@required GlobalKey trailingKey,
|
||||
@required Widget userTrailing,
|
||||
@required EdgeInsetsDirectional padding,
|
||||
@required Color actionsForegroundColor,
|
||||
}) {
|
||||
if (userTrailing == null) {
|
||||
return null;
|
||||
|
@ -1128,15 +1142,11 @@ class _NavigationBarStaticComponents {
|
|||
padding: EdgeInsetsDirectional.only(
|
||||
end: padding?.end ?? _kNavBarEdgePadding,
|
||||
),
|
||||
child: DefaultTextStyle(
|
||||
style: _navBarItemStyle(actionsForegroundColor),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(
|
||||
color: actionsForegroundColor,
|
||||
size: 32.0,
|
||||
),
|
||||
child: userTrailing,
|
||||
child: IconTheme.merge(
|
||||
data: const IconThemeData(
|
||||
size: 32.0,
|
||||
),
|
||||
child: userTrailing,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1188,24 +1198,24 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
|
|||
///
|
||||
/// The [color] parameter must not be null.
|
||||
const CupertinoNavigationBarBackButton({
|
||||
@required this.color,
|
||||
this.color,
|
||||
this.previousPageTitle,
|
||||
}) : _backChevron = null,
|
||||
_backLabel = null,
|
||||
assert(color != null);
|
||||
_backLabel = null;
|
||||
|
||||
// Allow the back chevron and label to be separately created (and keyed)
|
||||
// because they animate separately during page transitions.
|
||||
const CupertinoNavigationBarBackButton._assemble(
|
||||
this._backChevron,
|
||||
this._backLabel,
|
||||
this.color,
|
||||
) : previousPageTitle = null,
|
||||
assert(color != null);
|
||||
color = null;
|
||||
|
||||
/// The [Color] of the back button.
|
||||
///
|
||||
/// Must not be null.
|
||||
/// Can be used to override the color of the back button chevron and label.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color color;
|
||||
|
||||
/// An override for showing the previous route's title. If null, it will be
|
||||
|
@ -1225,16 +1235,21 @@ class CupertinoNavigationBarBackButton extends StatelessWidget {
|
|||
'CupertinoNavigationBarBackButton should only be used in routes that can be popped',
|
||||
);
|
||||
|
||||
TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle;
|
||||
if (color != null) {
|
||||
actionTextStyle = actionTextStyle.copyWith(color: color);
|
||||
}
|
||||
|
||||
return CupertinoButton(
|
||||
child: Semantics(
|
||||
container: true,
|
||||
excludeSemantics: true,
|
||||
label: 'Back',
|
||||
button: true,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
|
||||
child: DefaultTextStyle(
|
||||
style: _navBarItemStyle(color),
|
||||
child: DefaultTextStyle(
|
||||
style: actionTextStyle,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
@ -1367,18 +1382,23 @@ class _TransitionableNavigationBar extends StatelessWidget {
|
|||
_TransitionableNavigationBar({
|
||||
@required this.componentsKeys,
|
||||
@required this.backgroundColor,
|
||||
@required this.actionsForegroundColor,
|
||||
@required this.backButtonTextStyle,
|
||||
@required this.titleTextStyle,
|
||||
@required this.largeTitleTextStyle,
|
||||
@required this.border,
|
||||
@required this.hasUserMiddle,
|
||||
@required this.largeExpanded,
|
||||
@required this.child,
|
||||
}) : assert(componentsKeys != null),
|
||||
assert(largeExpanded != null),
|
||||
assert(!largeExpanded || largeTitleTextStyle != null),
|
||||
super(key: componentsKeys.navBarBoxKey);
|
||||
|
||||
final _NavigationBarStaticComponentsKeys componentsKeys;
|
||||
final Color backgroundColor;
|
||||
final Color actionsForegroundColor;
|
||||
final TextStyle backButtonTextStyle;
|
||||
final TextStyle titleTextStyle;
|
||||
final TextStyle largeTitleTextStyle;
|
||||
final Border border;
|
||||
final bool hasUserMiddle;
|
||||
final bool largeExpanded;
|
||||
|
@ -1558,8 +1578,12 @@ class _NavigationBarComponentsTransition {
|
|||
topComponents = topNavBar.componentsKeys,
|
||||
bottomNavBarBox = bottomNavBar.renderBox,
|
||||
topNavBarBox = topNavBar.renderBox,
|
||||
bottomActionsStyle = _navBarItemStyle(bottomNavBar.actionsForegroundColor),
|
||||
topActionsStyle = _navBarItemStyle(topNavBar.actionsForegroundColor),
|
||||
bottomBackButtonTextStyle = bottomNavBar.backButtonTextStyle,
|
||||
topBackButtonTextStyle = topNavBar.backButtonTextStyle,
|
||||
bottomTitleTextStyle = bottomNavBar.titleTextStyle,
|
||||
topTitleTextStyle = topNavBar.titleTextStyle,
|
||||
bottomLargeTitleTextStyle = bottomNavBar.largeTitleTextStyle,
|
||||
topLargeTitleTextStyle = topNavBar.largeTitleTextStyle,
|
||||
bottomHasUserMiddle = bottomNavBar.hasUserMiddle,
|
||||
topHasUserMiddle = topNavBar.hasUserMiddle,
|
||||
bottomLargeExpanded = bottomNavBar.largeExpanded,
|
||||
|
@ -1588,8 +1612,13 @@ class _NavigationBarComponentsTransition {
|
|||
final RenderBox bottomNavBarBox;
|
||||
final RenderBox topNavBarBox;
|
||||
|
||||
final TextStyle bottomActionsStyle;
|
||||
final TextStyle topActionsStyle;
|
||||
final TextStyle bottomBackButtonTextStyle;
|
||||
final TextStyle topBackButtonTextStyle;
|
||||
final TextStyle bottomTitleTextStyle;
|
||||
final TextStyle topTitleTextStyle;
|
||||
final TextStyle bottomLargeTitleTextStyle;
|
||||
final TextStyle topLargeTitleTextStyle;
|
||||
|
||||
final bool bottomHasUserMiddle;
|
||||
final bool topHasUserMiddle;
|
||||
final bool bottomLargeExpanded;
|
||||
|
@ -1702,7 +1731,7 @@ class _NavigationBarComponentsTransition {
|
|||
child: FadeTransition(
|
||||
opacity: fadeOutBy(0.6),
|
||||
child: DefaultTextStyle(
|
||||
style: bottomActionsStyle,
|
||||
style: bottomBackButtonTextStyle,
|
||||
child: bottomBackChevron.child,
|
||||
),
|
||||
),
|
||||
|
@ -1734,7 +1763,7 @@ class _NavigationBarComponentsTransition {
|
|||
child: FadeTransition(
|
||||
opacity: fadeOutBy(0.2),
|
||||
child: DefaultTextStyle(
|
||||
style: bottomActionsStyle,
|
||||
style: bottomBackButtonTextStyle,
|
||||
child: bottomBackLabel.child,
|
||||
),
|
||||
),
|
||||
|
@ -1770,8 +1799,8 @@ class _NavigationBarComponentsTransition {
|
|||
alignment: AlignmentDirectional.centerStart,
|
||||
child: DefaultTextStyleTransition(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kMiddleTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
begin: bottomTitleTextStyle,
|
||||
end: topBackButtonTextStyle,
|
||||
)),
|
||||
child: bottomMiddle.child,
|
||||
),
|
||||
|
@ -1790,7 +1819,7 @@ class _NavigationBarComponentsTransition {
|
|||
opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7),
|
||||
// Keep the font when transitioning into a non-back label leading.
|
||||
child: DefaultTextStyle(
|
||||
style: _kMiddleTitleTextStyle,
|
||||
style: bottomTitleTextStyle,
|
||||
child: bottomMiddle.child,
|
||||
),
|
||||
),
|
||||
|
@ -1826,8 +1855,8 @@ class _NavigationBarComponentsTransition {
|
|||
alignment: AlignmentDirectional.centerStart,
|
||||
child: DefaultTextStyleTransition(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kLargeTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
begin: bottomLargeTitleTextStyle,
|
||||
end: topBackButtonTextStyle,
|
||||
)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
@ -1861,7 +1890,7 @@ class _NavigationBarComponentsTransition {
|
|||
opacity: fadeOutBy(0.4),
|
||||
// Keep the font when transitioning into a non-back-label leading.
|
||||
child: DefaultTextStyle(
|
||||
style: _kLargeTitleTextStyle,
|
||||
style: bottomLargeTitleTextStyle,
|
||||
child: bottomLargeTitle.child,
|
||||
),
|
||||
),
|
||||
|
@ -1936,7 +1965,7 @@ class _NavigationBarComponentsTransition {
|
|||
child: FadeTransition(
|
||||
opacity: fadeInFrom(bottomBackChevron == null ? 0.7 : 0.4),
|
||||
child: DefaultTextStyle(
|
||||
style: topActionsStyle,
|
||||
style: topBackButtonTextStyle,
|
||||
child: topBackChevron.child,
|
||||
),
|
||||
),
|
||||
|
@ -1985,8 +2014,8 @@ class _NavigationBarComponentsTransition {
|
|||
opacity: midClickOpacity ?? fadeInFrom(0.4),
|
||||
child: DefaultTextStyleTransition(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kLargeTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
begin: bottomLargeTitleTextStyle,
|
||||
end: topBackButtonTextStyle,
|
||||
)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
@ -2010,8 +2039,8 @@ class _NavigationBarComponentsTransition {
|
|||
opacity: midClickOpacity ?? fadeInFrom(0.3),
|
||||
child: DefaultTextStyleTransition(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kMiddleTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
begin: bottomTitleTextStyle,
|
||||
end: topBackButtonTextStyle,
|
||||
)),
|
||||
child: topBackLabel.child,
|
||||
),
|
||||
|
@ -2053,7 +2082,7 @@ class _NavigationBarComponentsTransition {
|
|||
child: FadeTransition(
|
||||
opacity: fadeInFrom(0.25),
|
||||
child: DefaultTextStyle(
|
||||
style: _kMiddleTitleTextStyle,
|
||||
style: topTitleTextStyle,
|
||||
child: topMiddle.child,
|
||||
),
|
||||
),
|
||||
|
@ -2101,7 +2130,7 @@ class _NavigationBarComponentsTransition {
|
|||
child: FadeTransition(
|
||||
opacity: fadeInFrom(0.3),
|
||||
child: DefaultTextStyle(
|
||||
style: _kLargeTitleTextStyle,
|
||||
style: topLargeTitleTextStyle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: topLargeTitle.child,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
/// Implements a single iOS application page's layout.
|
||||
///
|
||||
|
@ -21,7 +21,7 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||
const CupertinoPageScaffold({
|
||||
Key key,
|
||||
this.navigationBar,
|
||||
this.backgroundColor = CupertinoColors.white,
|
||||
this.backgroundColor,
|
||||
this.resizeToAvoidBottomInset = true,
|
||||
@required this.child,
|
||||
}) : assert(child != null),
|
||||
|
@ -48,7 +48,7 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||
|
||||
/// The color of the widget that underlies the entire scaffold.
|
||||
///
|
||||
/// By default uses [CupertinoColors.white] color.
|
||||
/// By default uses [CupertinoTheme]'s `scaffoldBackgroundColor` when null.
|
||||
final Color backgroundColor;
|
||||
|
||||
/// Whether the [child] should size itself to avoid the window's bottom inset.
|
||||
|
@ -78,10 +78,13 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||
? existingMediaQuery.viewInsets.bottom
|
||||
: 0.0;
|
||||
|
||||
final bool fullObstruction =
|
||||
navigationBar.fullObstruction ?? CupertinoTheme.of(context).barBackgroundColor.alpha == 0xFF;
|
||||
|
||||
// If navigation bar is opaquely obstructing, directly shift the main content
|
||||
// down. If translucent, let main content draw behind navigation bar but hint the
|
||||
// obstructed area.
|
||||
if (navigationBar.fullObstruction) {
|
||||
if (fullObstruction) {
|
||||
paddedContent = Padding(
|
||||
padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding),
|
||||
child: child,
|
||||
|
@ -114,7 +117,9 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||
}
|
||||
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(color: backgroundColor),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? CupertinoTheme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Stack(
|
||||
children: stacked,
|
||||
),
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Minimum padding from horizontal edges of segmented control to edges of
|
||||
// encompassing widget.
|
||||
|
@ -56,9 +56,8 @@ const Duration _kFadeDuration = Duration(milliseconds: 165);
|
|||
///
|
||||
/// A segmented control may optionally be created with custom colors. The
|
||||
/// [unselectedColor], [selectedColor], [borderColor], and [pressedColor]
|
||||
/// arguments can be used to change the segmented control's colors from
|
||||
/// [CupertinoColors.activeBlue] and [CupertinoColors.white] to a custom
|
||||
/// configuration.
|
||||
/// arguments can be used to override the segmented control's colors from
|
||||
/// [CupertinoTheme] defaults.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
|
@ -66,8 +65,7 @@ const Duration _kFadeDuration = Duration(milliseconds: 165);
|
|||
class CupertinoSegmentedControl<T> extends StatefulWidget {
|
||||
/// Creates an iOS-style segmented control bar.
|
||||
///
|
||||
/// The [children], [onValueChanged], [unselectedColor], [selectedColor],
|
||||
/// [borderColor], and [pressedColor] arguments must not be null. The
|
||||
/// The [children] and [onValueChanged] arguments must not be null. The
|
||||
/// [children] argument must be an ordered [Map] such as a [LinkedHashMap].
|
||||
/// Further, the length of the [children] list must be greater than one.
|
||||
///
|
||||
|
@ -85,19 +83,17 @@ class CupertinoSegmentedControl<T> extends StatefulWidget {
|
|||
@required this.children,
|
||||
@required this.onValueChanged,
|
||||
this.groupValue,
|
||||
this.unselectedColor = CupertinoColors.white,
|
||||
this.selectedColor = CupertinoColors.activeBlue,
|
||||
this.borderColor = CupertinoColors.activeBlue,
|
||||
this.pressedColor = const Color(0x33007AFF),
|
||||
this.unselectedColor,
|
||||
this.selectedColor,
|
||||
this.borderColor,
|
||||
this.pressedColor,
|
||||
}) : assert(children != null),
|
||||
assert(children.length >= 2),
|
||||
assert(onValueChanged != null),
|
||||
assert(groupValue == null || children.keys.any((T child) => child == groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.'),
|
||||
assert(unselectedColor != null),
|
||||
assert(selectedColor != null),
|
||||
assert(borderColor != null),
|
||||
assert(pressedColor != null),
|
||||
assert(
|
||||
groupValue == null || children.keys.any((T child) => child == groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// The identifying keys and corresponding widget values in the
|
||||
|
@ -163,36 +159,24 @@ class CupertinoSegmentedControl<T> extends StatefulWidget {
|
|||
/// The color used to fill the backgrounds of unselected widgets and as the
|
||||
/// text color of the selected widget.
|
||||
///
|
||||
/// This attribute must not be null.
|
||||
///
|
||||
/// If this attribute is unspecified, this color will default to
|
||||
/// [CupertinoColors.white].
|
||||
/// Defaults to [CupertinoTheme]'s `primaryContrastingColor` if null.
|
||||
final Color unselectedColor;
|
||||
|
||||
/// The color used to fill the background of the selected widget and as the text
|
||||
/// color of unselected widgets.
|
||||
///
|
||||
/// This attribute must not be null.
|
||||
///
|
||||
/// If this attribute is unspecified, this color will default to
|
||||
/// [CupertinoColors.activeBlue].
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color selectedColor;
|
||||
|
||||
/// The color used as the border around each widget.
|
||||
///
|
||||
/// This attribute must not be null.
|
||||
///
|
||||
/// If this attribute is unspecified, this color will default to
|
||||
/// [CupertinoColors.activeBlue].
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color borderColor;
|
||||
|
||||
/// The color used to fill the background of the widget the user is
|
||||
/// temporarily interacting with through a long press or drag.
|
||||
///
|
||||
/// This attribute must not be null.
|
||||
///
|
||||
/// If this attribute is unspecified, this color will default to
|
||||
/// `Color(0x33007AFF)`, a light, partially-transparent blue color.
|
||||
/// Defaults to the selectedColor at 20% opacity if null.
|
||||
final Color pressedColor;
|
||||
|
||||
@override
|
||||
|
@ -210,21 +194,68 @@ class _SegmentedControlState<T> extends State<CupertinoSegmentedControl<T>>
|
|||
ColorTween _reverseBackgroundColorTween;
|
||||
ColorTween _textColorTween;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Color _selectedColor;
|
||||
Color _unselectedColor;
|
||||
Color _borderColor;
|
||||
Color _pressedColor;
|
||||
|
||||
AnimationController createAnimationController() {
|
||||
return AnimationController(
|
||||
duration: _kFadeDuration,
|
||||
vsync: this,
|
||||
)..addListener(() {
|
||||
setState(() {
|
||||
// State of background/text colors has changed
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool _updateColors() {
|
||||
assert(mounted, 'This should only be called after didUpdateDependencies');
|
||||
bool changed = false;
|
||||
final Color selectedColor = widget.selectedColor ?? CupertinoTheme.of(context).primaryColor;
|
||||
if (_selectedColor != selectedColor) {
|
||||
changed = true;
|
||||
_selectedColor = selectedColor;
|
||||
}
|
||||
final Color unselectedColor = widget.unselectedColor ?? CupertinoTheme.of(context).primaryContrastingColor;
|
||||
if (_unselectedColor != unselectedColor) {
|
||||
changed = true;
|
||||
_unselectedColor = unselectedColor;
|
||||
}
|
||||
final Color borderColor = widget.borderColor ?? CupertinoTheme.of(context).primaryColor;
|
||||
if (_borderColor != borderColor) {
|
||||
changed = true;
|
||||
_borderColor = borderColor;
|
||||
}
|
||||
final Color pressedColor = widget.pressedColor ?? CupertinoTheme.of(context).primaryColor.withOpacity(0.2);
|
||||
if (_pressedColor != pressedColor) {
|
||||
changed = true;
|
||||
_pressedColor = pressedColor;
|
||||
}
|
||||
|
||||
_forwardBackgroundColorTween = ColorTween(
|
||||
begin: widget.pressedColor,
|
||||
end: widget.selectedColor,
|
||||
begin: _pressedColor,
|
||||
end: _selectedColor,
|
||||
);
|
||||
_reverseBackgroundColorTween = ColorTween(
|
||||
begin: widget.unselectedColor,
|
||||
end: widget.selectedColor,
|
||||
begin: _unselectedColor,
|
||||
end: _selectedColor,
|
||||
);
|
||||
_textColorTween = ColorTween(
|
||||
begin: widget.selectedColor,
|
||||
end: widget.unselectedColor,
|
||||
begin: _selectedColor,
|
||||
end: _unselectedColor,
|
||||
);
|
||||
return changed;
|
||||
}
|
||||
|
||||
void _updateAnimationControllers() {
|
||||
assert(mounted, 'This should only be called after didUpdateDependencies');
|
||||
for (AnimationController controller in _selectionControllers) {
|
||||
controller.dispose();
|
||||
}
|
||||
_selectionControllers.clear();
|
||||
_childTweens.clear();
|
||||
|
||||
for (T key in widget.children.keys) {
|
||||
final AnimationController animationController = createAnimationController();
|
||||
|
@ -238,15 +269,36 @@ class _SegmentedControlState<T> extends State<CupertinoSegmentedControl<T>>
|
|||
}
|
||||
}
|
||||
|
||||
AnimationController createAnimationController() {
|
||||
return AnimationController(
|
||||
duration: _kFadeDuration,
|
||||
vsync: this,
|
||||
)..addListener(() {
|
||||
setState(() {
|
||||
// State of background/text colors has changed
|
||||
});
|
||||
});
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
if (_updateColors()) {
|
||||
_updateAnimationControllers();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoSegmentedControl<T> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (_updateColors() || oldWidget.children.length != widget.children.length) {
|
||||
_updateAnimationControllers();
|
||||
}
|
||||
|
||||
if (oldWidget.groupValue != widget.groupValue) {
|
||||
int index = 0;
|
||||
for (T key in widget.children.keys) {
|
||||
if (widget.groupValue == key) {
|
||||
_childTweens[index] = _forwardBackgroundColorTween;
|
||||
_selectionControllers[index].forward();
|
||||
} else {
|
||||
_childTweens[index] = _reverseBackgroundColorTween;
|
||||
_selectionControllers[index].reverse();
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -257,6 +309,7 @@ class _SegmentedControlState<T> extends State<CupertinoSegmentedControl<T>>
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void _onTapDown(T currentKey) {
|
||||
if (_pressedKey == null && currentKey != widget.groupValue) {
|
||||
setState(() {
|
||||
|
@ -282,53 +335,18 @@ class _SegmentedControlState<T> extends State<CupertinoSegmentedControl<T>>
|
|||
if (_selectionControllers[index].isAnimating)
|
||||
return _textColorTween.evaluate(_selectionControllers[index]);
|
||||
if (widget.groupValue == currentKey)
|
||||
return widget.unselectedColor;
|
||||
return widget.selectedColor;
|
||||
return _unselectedColor;
|
||||
return _selectedColor;
|
||||
}
|
||||
|
||||
Color getBackgroundColor(int index, T currentKey) {
|
||||
if (_selectionControllers[index].isAnimating)
|
||||
return _childTweens[index].evaluate(_selectionControllers[index]);
|
||||
if (widget.groupValue == currentKey)
|
||||
return widget.selectedColor;
|
||||
return _selectedColor;
|
||||
if (_pressedKey == currentKey)
|
||||
return widget.pressedColor;
|
||||
return widget.unselectedColor;
|
||||
}
|
||||
|
||||
void updateAnimationControllers() {
|
||||
if (_selectionControllers.length > widget.children.length) {
|
||||
_selectionControllers.length = widget.children.length;
|
||||
_childTweens.length = widget.children.length;
|
||||
} else {
|
||||
for (int index = _selectionControllers.length; index < widget.children.length; index += 1) {
|
||||
_selectionControllers.add(createAnimationController());
|
||||
_childTweens.add(_reverseBackgroundColorTween);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoSegmentedControl<T> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (oldWidget.children.length != widget.children.length) {
|
||||
updateAnimationControllers();
|
||||
}
|
||||
|
||||
if (oldWidget.groupValue != widget.groupValue) {
|
||||
int index = 0;
|
||||
for (T key in widget.children.keys) {
|
||||
if (widget.groupValue == key) {
|
||||
_childTweens[index] = _forwardBackgroundColorTween;
|
||||
_selectionControllers[index].forward();
|
||||
} else {
|
||||
_childTweens[index] = _reverseBackgroundColorTween;
|
||||
_selectionControllers[index].reverse();
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return _pressedColor;
|
||||
return _unselectedColor;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -385,7 +403,7 @@ class _SegmentedControlState<T> extends State<CupertinoSegmentedControl<T>>
|
|||
selectedIndex: selectedIndex,
|
||||
pressedIndex: pressedIndex,
|
||||
backgroundColors: _backgroundColors,
|
||||
borderColor: widget.borderColor,
|
||||
borderColor: _borderColor,
|
||||
);
|
||||
|
||||
return Padding(
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:flutter/gestures.dart';
|
|||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'theme.dart';
|
||||
import 'thumb_painter.dart';
|
||||
|
||||
// Examples can assume:
|
||||
|
@ -189,7 +189,7 @@ class CupertinoSlider extends StatefulWidget {
|
|||
|
||||
/// The color to use for the portion of the slider that has been selected.
|
||||
///
|
||||
/// Defaults to [CupertinoColors.activeBlue].
|
||||
/// Defaults to the [CupertinoTheme]'s primary color if null.
|
||||
final Color activeColor;
|
||||
|
||||
@override
|
||||
|
@ -228,7 +228,7 @@ class _CupertinoSliderState extends State<CupertinoSlider> with TickerProviderSt
|
|||
return _CupertinoSliderRenderObjectWidget(
|
||||
value: (widget.value - widget.min) / (widget.max - widget.min),
|
||||
divisions: widget.divisions,
|
||||
activeColor: widget.activeColor ?? CupertinoColors.activeBlue,
|
||||
activeColor: widget.activeColor ?? CupertinoTheme.of(context).primaryColor,
|
||||
onChanged: widget.onChanged != null ? _handleChanged : null,
|
||||
onChangeStart: widget.onChangeStart != null ? _handleDragStart : null,
|
||||
onChangeEnd: widget.onChangeEnd != null ? _handleDragEnd : null,
|
||||
|
@ -485,15 +485,14 @@ class _RenderCupertinoSlider extends RenderConstrainedBox {
|
|||
final double trackActive = offset.dx + _thumbCenter;
|
||||
|
||||
final Canvas canvas = context.canvas;
|
||||
final Paint paint = Paint();
|
||||
|
||||
if (visualPosition > 0.0) {
|
||||
paint.color = rightColor;
|
||||
final Paint paint = Paint()..color = rightColor;
|
||||
canvas.drawRRect(RRect.fromLTRBXY(trackLeft, trackTop, trackActive, trackBottom, 1.0, 1.0), paint);
|
||||
}
|
||||
|
||||
if (visualPosition < 1.0) {
|
||||
paint.color = leftColor;
|
||||
final Paint paint = Paint()..color = leftColor;
|
||||
canvas.drawRRect(RRect.fromLTRBXY(trackActive, trackTop, trackRight, trackBottom, 1.0, 1.0), paint);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,8 @@ class CupertinoSwitch extends StatefulWidget {
|
|||
|
||||
/// The color to use when this switch is on.
|
||||
///
|
||||
/// Defaults to [CupertinoColors.activeGreen].
|
||||
/// Defaults to [CupertinoColors.activeGreen] when null and ignores the
|
||||
/// [CupertinoTheme] in accordance to native iOS behavior.
|
||||
final Color activeColor;
|
||||
|
||||
@override
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'bottom_tab_bar.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
/// Implements a tabbed iOS application's root layout and behavior structure.
|
||||
///
|
||||
|
@ -173,13 +174,13 @@ class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
|
|||
|
||||
// TODO(xster): Use real size after partial layout instead of preferred size.
|
||||
// https://github.com/flutter/flutter/issues/12912
|
||||
final double bottomPadding = widget.tabBar.preferredSize.height
|
||||
+ existingMediaQuery.padding.bottom;
|
||||
final double bottomPadding =
|
||||
widget.tabBar.preferredSize.height + existingMediaQuery.padding.bottom;
|
||||
|
||||
// If tab bar opaque, directly stop the main content higher. If
|
||||
// translucent, let main content draw behind the tab bar but hint the
|
||||
// obstructed area.
|
||||
if (widget.tabBar.opaque) {
|
||||
if (widget.tabBar.opaque(context)) {
|
||||
content = Padding(
|
||||
padding: EdgeInsets.only(bottom: bottomPadding),
|
||||
child: content,
|
||||
|
@ -219,8 +220,13 @@ class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
|
|||
));
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: stacked,
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: CupertinoTheme.of(context).scaffoldBackgroundColor
|
||||
),
|
||||
child: Stack(
|
||||
children: stacked,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
|
|||
import 'colors.dart';
|
||||
import 'icons.dart';
|
||||
import 'text_selection.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
export 'package:flutter/services.dart' show TextInputType, TextInputAction, TextCapitalization;
|
||||
|
||||
|
@ -32,15 +33,6 @@ const BoxDecoration _kDefaultRoundedBorderDecoration = BoxDecoration(
|
|||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||
);
|
||||
|
||||
// Default iOS style from HIG specs with larger font.
|
||||
const TextStyle _kDefaultTextStyle = TextStyle(
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.38,
|
||||
color: CupertinoColors.black,
|
||||
decoration: TextDecoration.none,
|
||||
);
|
||||
|
||||
// Value extracted via color reader from iOS simulator.
|
||||
const Color _kSelectionHighlightColor = Color(0x667FAACF);
|
||||
const Color _kInactiveTextColor = Color(0xFFC2C2C2);
|
||||
|
@ -160,7 +152,7 @@ class CupertinoTextField extends StatefulWidget {
|
|||
TextInputType keyboardType,
|
||||
this.textInputAction,
|
||||
this.textCapitalization = TextCapitalization.none,
|
||||
this.style = _kDefaultTextStyle,
|
||||
this.style,
|
||||
this.textAlign = TextAlign.start,
|
||||
this.autofocus = false,
|
||||
this.obscureText = false,
|
||||
|
@ -271,7 +263,7 @@ class CupertinoTextField extends StatefulWidget {
|
|||
///
|
||||
/// Also serves as a base for the [placeholder] text's style.
|
||||
///
|
||||
/// Defaults to a standard iOS style and cannot be null.
|
||||
/// Defaults to the standard iOS font style from [CupertinoTheme] if null.
|
||||
final TextStyle style;
|
||||
|
||||
/// {@macro flutter.widgets.editableText.textAlign}
|
||||
|
@ -558,7 +550,9 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
);
|
||||
}
|
||||
|
||||
Widget _addTextDependentAttachments(Widget editableText) {
|
||||
Widget _addTextDependentAttachments(Widget editableText, TextStyle textStyle) {
|
||||
assert(editableText != null);
|
||||
assert(textStyle != null);
|
||||
// If there are no surrounding widgets, just return the core editable text
|
||||
// part.
|
||||
if (widget.placeholder == null &&
|
||||
|
@ -593,7 +587,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
widget.placeholder,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: widget.style.merge(
|
||||
style: textStyle.merge(
|
||||
const TextStyle(
|
||||
color: _kInactiveTextColor,
|
||||
fontWeight: FontWeight.w300,
|
||||
|
@ -637,14 +631,13 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
Widget build(BuildContext context) {
|
||||
super.build(context); // See AutomaticKeepAliveClientMixin.
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
final Brightness keyboardAppearance = widget.keyboardAppearance;
|
||||
final TextEditingController controller = _effectiveController;
|
||||
final FocusNode focusNode = _effectiveFocusNode;
|
||||
final List<TextInputFormatter> formatters = widget.inputFormatters ?? <TextInputFormatter>[];
|
||||
final bool enabled = widget.enabled ?? true;
|
||||
if (widget.maxLength != null && widget.maxLengthEnforced) {
|
||||
formatters.add(LengthLimitingTextInputFormatter(widget.maxLength));
|
||||
}
|
||||
final TextStyle textStyle = widget.style ?? CupertinoTheme.of(context).textTheme.textStyle;
|
||||
|
||||
final Widget paddedEditable = Padding(
|
||||
padding: widget.padding,
|
||||
|
@ -652,11 +645,11 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
child: EditableText(
|
||||
key: _editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
focusNode: _effectiveFocusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.textInputAction,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
style: widget.style,
|
||||
style: textStyle,
|
||||
textAlign: widget.textAlign,
|
||||
autofocus: widget.autofocus,
|
||||
obscureText: widget.obscureText,
|
||||
|
@ -674,7 +667,7 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
cursorColor: widget.cursorColor,
|
||||
backgroundCursorColor: CupertinoColors.inactiveGray,
|
||||
scrollPadding: widget.scrollPadding,
|
||||
keyboardAppearance: keyboardAppearance,
|
||||
keyboardAppearance: widget.keyboardAppearance,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -692,14 +685,18 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK
|
|||
decoration: widget.decoration,
|
||||
// The main decoration and the disabled scrim exists separately.
|
||||
child: Container(
|
||||
color: enabled ? null : _kDisabledBackground,
|
||||
color: enabled
|
||||
? null
|
||||
: CupertinoTheme.of(context).brightness == Brightness.light
|
||||
? _kDisabledBackground
|
||||
: CupertinoColors.darkBackgroundGray,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTapDown: _handleTapDown,
|
||||
onTapUp: _handleTapUp,
|
||||
onLongPress: _handleLongPress,
|
||||
excludeFromSemantics: true,
|
||||
child: _addTextDependentAttachments(paddedEditable),
|
||||
child: _addTextDependentAttachments(paddedEditable, textStyle),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
181
packages/flutter/lib/src/cupertino/text_theme.dart
Normal file
181
packages/flutter/lib/src/cupertino/text_theme.dart
Normal file
|
@ -0,0 +1,181 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart' show Brightness;
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
|
||||
// Values derived from https://developer.apple.com/design/resources/.
|
||||
const TextStyle _kDefaultLightTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.41,
|
||||
color: CupertinoColors.black,
|
||||
decoration: TextDecoration.none,
|
||||
);
|
||||
|
||||
// Values derived from https://developer.apple.com/design/resources/.
|
||||
const TextStyle _kDefaultDarkTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.41,
|
||||
color: CupertinoColors.white,
|
||||
decoration: TextDecoration.none,
|
||||
);
|
||||
|
||||
// Values derived from https://developer.apple.com/design/resources/.
|
||||
const TextStyle _kDefaultActionTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.41,
|
||||
color: CupertinoColors.activeBlue,
|
||||
decoration: TextDecoration.none,
|
||||
);
|
||||
|
||||
// Values derived from https://developer.apple.com/design/resources/.
|
||||
const TextStyle _kDefaultTabLabelTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 10.0,
|
||||
letterSpacing: -0.24,
|
||||
color: CupertinoColors.inactiveGray,
|
||||
);
|
||||
|
||||
const TextStyle _kDefaultMiddleTitleLightTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.41,
|
||||
color: CupertinoColors.black,
|
||||
);
|
||||
|
||||
const TextStyle _kDefaultMiddleTitleDarkTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Text',
|
||||
fontSize: 17.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.41,
|
||||
color: CupertinoColors.white,
|
||||
);
|
||||
|
||||
const TextStyle _kDefaultLargeTitleLightTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Display',
|
||||
fontSize: 34.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.41,
|
||||
color: CupertinoColors.black,
|
||||
);
|
||||
|
||||
const TextStyle _kDefaultLargeTitleDarkTextStyle = TextStyle(
|
||||
inherit: false,
|
||||
fontFamily: '.SF Pro Display',
|
||||
fontSize: 34.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.41,
|
||||
color: CupertinoColors.white,
|
||||
);
|
||||
|
||||
/// Cupertino typography theme in a [CupertinoThemeData].
|
||||
@immutable
|
||||
class CupertinoTextThemeData extends Diagnosticable {
|
||||
/// Create a [CupertinoTextThemeData].
|
||||
///
|
||||
/// The [primaryColor] and [isLight] parameters are used to derive TextStyle
|
||||
/// defaults of other attributes such as [textStyle] and [actionTextStyle]
|
||||
/// etc. The default value of [primaryColor] is [CupertinoColors.activeBlue]
|
||||
/// and the default value of [isLight] is true.
|
||||
///
|
||||
/// Other [TextStyle] parameters default to default iOS text styles when
|
||||
/// unspecified.
|
||||
const CupertinoTextThemeData({
|
||||
Color primaryColor,
|
||||
Brightness brightness,
|
||||
TextStyle textStyle,
|
||||
TextStyle actionTextStyle,
|
||||
TextStyle tabLabelTextStyle,
|
||||
TextStyle navTitleTextStyle,
|
||||
TextStyle navLargeTitleTextStyle,
|
||||
TextStyle navActionTextStyle,
|
||||
}) : _primaryColor = primaryColor ?? CupertinoColors.activeBlue,
|
||||
_brightness = brightness,
|
||||
_textStyle = textStyle,
|
||||
_actionTextStyle = actionTextStyle,
|
||||
_tabLabelTextStyle = tabLabelTextStyle,
|
||||
_navTitleTextStyle = navTitleTextStyle,
|
||||
_navLargeTitleTextStyle = navLargeTitleTextStyle,
|
||||
_navActionTextStyle = navActionTextStyle;
|
||||
|
||||
final Color _primaryColor;
|
||||
final Brightness _brightness;
|
||||
bool get _isLight => _brightness != Brightness.dark;
|
||||
|
||||
final TextStyle _textStyle;
|
||||
/// Typography of general text content for Cupertino widgets.
|
||||
TextStyle get textStyle => _textStyle ?? (_isLight ? _kDefaultLightTextStyle : _kDefaultDarkTextStyle);
|
||||
|
||||
final TextStyle _actionTextStyle;
|
||||
/// Typography of interactive text content such as text in a button without background.
|
||||
TextStyle get actionTextStyle {
|
||||
return _actionTextStyle ?? _kDefaultActionTextStyle.copyWith(
|
||||
color: _primaryColor,
|
||||
);
|
||||
}
|
||||
|
||||
final TextStyle _tabLabelTextStyle;
|
||||
/// Typography of unselected tabs.
|
||||
TextStyle get tabLabelTextStyle => _tabLabelTextStyle ?? _kDefaultTabLabelTextStyle;
|
||||
|
||||
final TextStyle _navTitleTextStyle;
|
||||
/// Typography of titles in standard navigation bars.
|
||||
TextStyle get navTitleTextStyle {
|
||||
return _navTitleTextStyle ??
|
||||
(_isLight ? _kDefaultMiddleTitleLightTextStyle : _kDefaultMiddleTitleDarkTextStyle);
|
||||
}
|
||||
|
||||
final TextStyle _navLargeTitleTextStyle;
|
||||
/// Typography of large titles in sliver navigation bars.
|
||||
TextStyle get navLargeTitleTextStyle {
|
||||
return _navLargeTitleTextStyle ??
|
||||
(_isLight ? _kDefaultLargeTitleLightTextStyle : _kDefaultLargeTitleDarkTextStyle);
|
||||
}
|
||||
|
||||
final TextStyle _navActionTextStyle;
|
||||
/// Typography of interative text content in navigation bars.
|
||||
TextStyle get navActionTextStyle {
|
||||
return _navActionTextStyle ?? _kDefaultActionTextStyle.copyWith(
|
||||
color: _primaryColor,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a copy of the current [CupertinoTextThemeData] instance with
|
||||
/// specified overrides.
|
||||
CupertinoTextThemeData copyWith({
|
||||
Color primaryColor,
|
||||
Brightness brightness,
|
||||
TextStyle textStyle,
|
||||
TextStyle actionTextStyle,
|
||||
TextStyle tabLabelTextStyle,
|
||||
TextStyle navTitleTextStyle,
|
||||
TextStyle navLargeTitleTextStyle,
|
||||
TextStyle navActionTextStyle,
|
||||
}) {
|
||||
return CupertinoTextThemeData(
|
||||
primaryColor: primaryColor ?? _primaryColor,
|
||||
brightness: brightness ?? _brightness,
|
||||
textStyle: textStyle ?? _textStyle,
|
||||
actionTextStyle: actionTextStyle ?? _actionTextStyle,
|
||||
tabLabelTextStyle: tabLabelTextStyle ?? _tabLabelTextStyle,
|
||||
navTitleTextStyle: navTitleTextStyle ?? _navTitleTextStyle,
|
||||
navLargeTitleTextStyle: navLargeTitleTextStyle ?? _navLargeTitleTextStyle,
|
||||
navActionTextStyle: navActionTextStyle ?? _navActionTextStyle,
|
||||
);
|
||||
}
|
||||
}
|
263
packages/flutter/lib/src/cupertino/theme.dart
Normal file
263
packages/flutter/lib/src/cupertino/theme.dart
Normal file
|
@ -0,0 +1,263 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'text_theme.dart';
|
||||
|
||||
export 'package:flutter/services.dart' show Brightness;
|
||||
|
||||
// Values derived from https://developer.apple.com/design/resources/.
|
||||
const Color _kDefaultBarLightBackgroundColor = Color(0xCCF8F8F8);
|
||||
const Color _kDefaultBarDarkBackgroundColor = Color(0xB7212121);
|
||||
|
||||
/// Applies a visual styling theme to descendant Cupertino widgets.
|
||||
///
|
||||
/// Affects the color and text styles of Cupertino widgets whose styling
|
||||
/// are not overridden when constructing the respective widgets instances.
|
||||
///
|
||||
/// Descendant widgets can retrieve the current [CupertinoThemeData] by calling
|
||||
/// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when
|
||||
/// an ancestor [CupertinoThemeData] is retrieved via [CupertinoTheme.of].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoThemeData], specifies the theme's visual styling.
|
||||
/// * [CupertinoApp], which will automatically add a [CupertinoTheme].
|
||||
/// * [Theme], a Material theme which will automatically add a [CupertinoTheme]
|
||||
/// with a [CupertinoThemeData] derived from the Material [ThemeData].
|
||||
class CupertinoTheme extends InheritedWidget {
|
||||
/// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling.
|
||||
///
|
||||
/// The [data] and [child] parameters must not be null.
|
||||
const CupertinoTheme({
|
||||
Key key,
|
||||
@required this.data,
|
||||
@required Widget child,
|
||||
}) : assert(child != null),
|
||||
assert(data != null),
|
||||
super(key: key, child: child);
|
||||
|
||||
/// The [CupertinoThemeData] styling for this theme.
|
||||
final CupertinoThemeData data;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(CupertinoTheme oldWidget) => data != oldWidget.data;
|
||||
|
||||
/// Retrieve the [CupertinoThemeData] from an ancestor [CupertinoTheme] widget.
|
||||
///
|
||||
/// Returns a default [CupertinoThemeData] if no [CupertinoTheme] widgets
|
||||
/// exist in the ancestry tree.
|
||||
static CupertinoThemeData of(BuildContext context) {
|
||||
final CupertinoTheme theme = context.inheritFromWidgetOfExactType(CupertinoTheme);
|
||||
return theme?.data ?? const CupertinoThemeData();
|
||||
}
|
||||
}
|
||||
|
||||
/// Styling specifications for a [CupertinoTheme].
|
||||
///
|
||||
/// All constructor parameters can be null, in which case a
|
||||
/// [CupertinoColors.activeBlue] based default iOS theme styling is used.
|
||||
///
|
||||
/// Parameters can also be partially specified, in which case some parameters
|
||||
/// will cascade down to other dependent parameters to create a cohesive
|
||||
/// visual effect. For instance, if a [primaryColor] is specified, it would
|
||||
/// cascade down to affect some fonts in [textTheme] if [textTheme] is not
|
||||
/// specified.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoTheme], in which this [CupertinoThemeData] is inserted.
|
||||
/// * [ThemeData], a Material equivalent that also configures Cupertino
|
||||
/// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData].
|
||||
@immutable
|
||||
class CupertinoThemeData extends Diagnosticable {
|
||||
/// Create a [CupertinoTheme] styling specification.
|
||||
///
|
||||
/// Unspecified parameters default to a reasonable iOS default style.
|
||||
const CupertinoThemeData({
|
||||
Brightness brightness,
|
||||
Color primaryColor,
|
||||
Color primaryContrastingColor,
|
||||
CupertinoTextThemeData textTheme,
|
||||
Color barBackgroundColor,
|
||||
Color scaffoldBackgroundColor,
|
||||
}) : this.raw(
|
||||
brightness,
|
||||
primaryColor,
|
||||
primaryContrastingColor,
|
||||
textTheme,
|
||||
barBackgroundColor,
|
||||
scaffoldBackgroundColor,
|
||||
);
|
||||
|
||||
/// Same as the default constructor but with positional arguments to avoid
|
||||
/// forgetting any and to specify all arguments.
|
||||
///
|
||||
/// Used by subclasses to get the superclass's defaulting behaviors.
|
||||
@protected
|
||||
const CupertinoThemeData.raw(
|
||||
this._brightness,
|
||||
this._primaryColor,
|
||||
this._primaryContrastingColor,
|
||||
this._textTheme,
|
||||
this._barBackgroundColor,
|
||||
this._scaffoldBackgroundColor,
|
||||
);
|
||||
|
||||
bool get _isLight => brightness == Brightness.light;
|
||||
|
||||
/// The general brightness theme of the [CupertinoThemeData].
|
||||
///
|
||||
/// Affects all other theme properties when unspecified. Defaults to
|
||||
/// [Brightness.light].
|
||||
///
|
||||
/// If coming from a Material [Theme] and unspecified, [brightness] will be
|
||||
/// derived from the Material [ThemeData]'s `brightness`.
|
||||
Brightness get brightness => _brightness ?? Brightness.light;
|
||||
final Brightness _brightness;
|
||||
|
||||
/// A color used on interactive elements of the theme.
|
||||
///
|
||||
/// This color is generally used on text and icons in buttons and tappable
|
||||
/// elements. Defaults to [CupertinoColors.activeBlue] or
|
||||
/// [CupertinoColors.activeOrange] when [brightness] is light or dark.
|
||||
///
|
||||
/// If coming from a Material [Theme] and unspecified, [primaryColor] will be
|
||||
/// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in
|
||||
/// iOS styling, the [primaryColor] is more sparsely used than in Material
|
||||
/// Design where the [primaryColor] can appear on non-interactive surfaces like
|
||||
/// the [AppBar] background, [TextField] borders etc.
|
||||
Color get primaryColor {
|
||||
return _primaryColor ??
|
||||
(_isLight ? CupertinoColors.activeBlue : CupertinoColors.activeOrange);
|
||||
}
|
||||
final Color _primaryColor;
|
||||
|
||||
/// A color used for content that must contrast against a [primaryColor] background.
|
||||
///
|
||||
/// For example, this color is used for a [CupertinoButton]'s text and icons
|
||||
/// when the button's background is [primaryColor].
|
||||
///
|
||||
/// If coming from a Material [Theme] and unspecified, [primaryContrastingColor]
|
||||
/// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`.
|
||||
Color get primaryContrastingColor {
|
||||
return _primaryContrastingColor ??
|
||||
(_isLight ? CupertinoColors.white : CupertinoColors.black);
|
||||
}
|
||||
final Color _primaryContrastingColor;
|
||||
|
||||
/// Text styles used by Cupertino widgets.
|
||||
///
|
||||
/// Derived from [brightness] and [primaryColor] if unspecified, including
|
||||
/// [brightness] and [primaryColor] of a Material [ThemeData] if coming
|
||||
/// from a Material [Theme].
|
||||
CupertinoTextThemeData get textTheme {
|
||||
return _textTheme ?? CupertinoTextThemeData(
|
||||
brightness: brightness,
|
||||
primaryColor: primaryColor,
|
||||
);
|
||||
}
|
||||
final CupertinoTextThemeData _textTheme;
|
||||
|
||||
/// Background color of the top nav bar and bottom tab bar.
|
||||
///
|
||||
/// Defaults to a light gray or a dark gray translucent color depending
|
||||
/// on the [brightness].
|
||||
Color get barBackgroundColor {
|
||||
return _barBackgroundColor ??
|
||||
(_isLight ? _kDefaultBarLightBackgroundColor : _kDefaultBarDarkBackgroundColor);
|
||||
}
|
||||
final Color _barBackgroundColor;
|
||||
|
||||
/// Background color of the scaffold.
|
||||
///
|
||||
/// Defaults to white or black depending on the [brightness].
|
||||
Color get scaffoldBackgroundColor {
|
||||
return _scaffoldBackgroundColor ??
|
||||
(_isLight ? CupertinoColors.white : CupertinoColors.black);
|
||||
}
|
||||
final Color _scaffoldBackgroundColor;
|
||||
|
||||
/// Return an instance of the [CupertinoThemeData] whose property getters
|
||||
/// only return the construction time specifications with no derived values.
|
||||
///
|
||||
/// Used in Material themes to let unspecified properties fallback to Material
|
||||
/// theme properties instead of iOS defaults.
|
||||
CupertinoThemeData noDefault() {
|
||||
return _NoDefaultCupertinoThemeData(
|
||||
_brightness,
|
||||
_primaryColor,
|
||||
_primaryContrastingColor,
|
||||
_textTheme,
|
||||
_barBackgroundColor,
|
||||
_scaffoldBackgroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a copy of [CupertinoThemeData] with specified attributes overridden.
|
||||
///
|
||||
/// Only the current instance's specified attributes are copied instead of
|
||||
/// derived values. For instance, if the current [primaryColor] is implied
|
||||
/// to be [CupertinoColors.activeOrange] due to the current [brightness],
|
||||
/// copying with a different [brightness] will also change the copy's
|
||||
/// implied [primaryColor].
|
||||
CupertinoThemeData copyWith({
|
||||
Brightness brightness,
|
||||
Color primaryColor,
|
||||
Color primaryContrastingColor,
|
||||
CupertinoTextThemeData textTheme,
|
||||
Color barBackgroundColor,
|
||||
Color scaffoldBackgroundColor,
|
||||
}) {
|
||||
return CupertinoThemeData(
|
||||
brightness: brightness ?? _brightness,
|
||||
primaryColor: primaryColor ?? _primaryColor,
|
||||
primaryContrastingColor: primaryContrastingColor ?? _primaryContrastingColor,
|
||||
textTheme: textTheme ?? _textTheme,
|
||||
barBackgroundColor: barBackgroundColor ?? _barBackgroundColor,
|
||||
scaffoldBackgroundColor: scaffoldBackgroundColor ?? _scaffoldBackgroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
const CupertinoThemeData defaultData = CupertinoThemeData();
|
||||
properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
|
||||
properties.add(DiagnosticsProperty<Color>('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
|
||||
properties.add(DiagnosticsProperty<Color>('primaryContrastingColor', primaryContrastingColor, defaultValue: defaultData.primaryContrastingColor));
|
||||
properties.add(DiagnosticsProperty<CupertinoTextThemeData>('textTheme', textTheme, defaultValue: defaultData.textTheme));
|
||||
properties.add(DiagnosticsProperty<Color>('barBackgroundColor', barBackgroundColor, defaultValue: defaultData.barBackgroundColor));
|
||||
properties.add(DiagnosticsProperty<Color>('scaffoldBackgroundColor', scaffoldBackgroundColor, defaultValue: defaultData.scaffoldBackgroundColor));
|
||||
}
|
||||
}
|
||||
|
||||
class _NoDefaultCupertinoThemeData extends CupertinoThemeData {
|
||||
const _NoDefaultCupertinoThemeData(
|
||||
this.brightness,
|
||||
this.primaryColor,
|
||||
this.primaryContrastingColor,
|
||||
this.textTheme,
|
||||
this.barBackgroundColor,
|
||||
this.scaffoldBackgroundColor,
|
||||
) : super.raw(
|
||||
brightness,
|
||||
primaryColor,
|
||||
primaryContrastingColor,
|
||||
textTheme,
|
||||
barBackgroundColor,
|
||||
scaffoldBackgroundColor,
|
||||
);
|
||||
|
||||
@override final Brightness brightness;
|
||||
@override final Color primaryColor;
|
||||
@override final Color primaryContrastingColor;
|
||||
@override final CupertinoTextThemeData textTheme;
|
||||
@override final Color barBackgroundColor;
|
||||
@override final Color scaffoldBackgroundColor;
|
||||
}
|
|
@ -443,7 +443,7 @@ class _MaterialAppState extends State<MaterialApp> {
|
|||
mini: true,
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
assert(() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
@ -143,7 +144,15 @@ class Theme extends StatelessWidget {
|
|||
theme: this,
|
||||
child: IconTheme(
|
||||
data: data.iconTheme,
|
||||
child: child,
|
||||
child: CupertinoTheme(
|
||||
// We're using a MaterialBasedCupertinoThemeData here instead of a
|
||||
// CupertinoThemeData because it defers some properties to the Material
|
||||
// ThemeData.
|
||||
data: MaterialBasedCupertinoThemeData(
|
||||
materialTheme: data,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'dart:ui' show Color, hashValues;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -153,6 +154,7 @@ class ThemeData extends Diagnosticable {
|
|||
ColorScheme colorScheme,
|
||||
DialogTheme dialogTheme,
|
||||
Typography typography,
|
||||
CupertinoThemeData cupertinoOverrideTheme
|
||||
}) {
|
||||
brightness ??= Brightness.light;
|
||||
final bool isDark = brightness == Brightness.dark;
|
||||
|
@ -246,6 +248,7 @@ class ThemeData extends Diagnosticable {
|
|||
labelStyle: textTheme.body2,
|
||||
);
|
||||
dialogTheme ??= const DialogTheme();
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
|
||||
return ThemeData.raw(
|
||||
brightness: brightness,
|
||||
|
@ -294,11 +297,13 @@ class ThemeData extends Diagnosticable {
|
|||
colorScheme: colorScheme,
|
||||
dialogTheme: dialogTheme,
|
||||
typography: typography,
|
||||
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a [ThemeData] given a set of exact values. All the values
|
||||
/// must be specified.
|
||||
/// Create a [ThemeData] given a set of exact values. All the values must be
|
||||
/// specified. They all must also be non-null except for
|
||||
/// [cupertinoOverrideTheme].
|
||||
///
|
||||
/// This will rarely be used directly. It is used by [lerp] to
|
||||
/// create intermediate themes based on two themes created with the
|
||||
|
@ -353,6 +358,7 @@ class ThemeData extends Diagnosticable {
|
|||
@required this.colorScheme,
|
||||
@required this.dialogTheme,
|
||||
@required this.typography,
|
||||
@required this.cupertinoOverrideTheme,
|
||||
}) : assert(brightness != null),
|
||||
assert(primaryColor != null),
|
||||
assert(primaryColorBrightness != null),
|
||||
|
@ -630,6 +636,18 @@ class ThemeData extends Diagnosticable {
|
|||
/// [primaryTextTheme], and [accentTextTheme].
|
||||
final Typography typography;
|
||||
|
||||
/// Components of the [CupertinoThemeData] to override from the Material
|
||||
/// [ThemeData] adaptation.
|
||||
///
|
||||
/// By default, [cupertinoOverrideTheme] is null and Cupertino widgets
|
||||
/// descendant to the Material [Theme] will adhere to a [CupertinoTheme]
|
||||
/// derived from the Material [ThemeData]. e.g. [ThemeData]'s [ColorTheme]
|
||||
/// will also inform the [CupertinoThemeData]'s `primaryColor` etc.
|
||||
///
|
||||
/// This cascading effect for individual attributes of the [CupertinoThemeData]
|
||||
/// can be overridden using attributes of this [cupertinoOverrideTheme].
|
||||
final CupertinoThemeData cupertinoOverrideTheme;
|
||||
|
||||
/// Creates a copy of this theme but with the given fields replaced with the new values.
|
||||
ThemeData copyWith({
|
||||
Brightness brightness,
|
||||
|
@ -678,7 +696,9 @@ class ThemeData extends Diagnosticable {
|
|||
ColorScheme colorScheme,
|
||||
DialogTheme dialogTheme,
|
||||
Typography typography,
|
||||
CupertinoThemeData cupertinoOverrideTheme,
|
||||
}) {
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
return ThemeData.raw(
|
||||
brightness: brightness ?? this.brightness,
|
||||
primaryColor: primaryColor ?? this.primaryColor,
|
||||
|
@ -726,6 +746,7 @@ class ThemeData extends Diagnosticable {
|
|||
colorScheme: colorScheme ?? this.colorScheme,
|
||||
dialogTheme: dialogTheme ?? this.dialogTheme,
|
||||
typography: typography ?? this.typography,
|
||||
cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -853,6 +874,7 @@ class ThemeData extends Diagnosticable {
|
|||
colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
|
||||
dialogTheme: DialogTheme.lerp(a.dialogTheme, b.dialogTheme, t),
|
||||
typography: Typography.lerp(a.typography, b.typography, t),
|
||||
cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -909,7 +931,8 @@ class ThemeData extends Diagnosticable {
|
|||
(otherData.pageTransitionsTheme == pageTransitionsTheme) &&
|
||||
(otherData.colorScheme == colorScheme) &&
|
||||
(otherData.dialogTheme == dialogTheme) &&
|
||||
(otherData.typography == typography);
|
||||
(otherData.typography == typography) &&
|
||||
(otherData.cupertinoOverrideTheme == cupertinoOverrideTheme);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -967,6 +990,7 @@ class ThemeData extends Diagnosticable {
|
|||
colorScheme,
|
||||
dialogTheme,
|
||||
typography,
|
||||
cupertinoOverrideTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1019,6 +1043,109 @@ class ThemeData extends Diagnosticable {
|
|||
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme));
|
||||
properties.add(DiagnosticsProperty<DialogTheme>('dialogTheme', dialogTheme, defaultValue: defaultData.dialogTheme));
|
||||
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
|
||||
properties.add(DiagnosticsProperty<CupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme));
|
||||
}
|
||||
}
|
||||
|
||||
/// A [CupertinoThemeData] that defers unspecified theme attributes to an
|
||||
/// upstream Material [ThemeData].
|
||||
///
|
||||
/// This type of [CupertinoThemeData] is used by the Material [Theme] to
|
||||
/// harmonize the [CupertinoTheme] with the material theme's colors and text
|
||||
/// styles.
|
||||
///
|
||||
/// In the most basic case, [ThemeData]'s `cupertinoOverrideTheme` is null and
|
||||
/// and descendant Cupertino widgets' styling is derived from the Material theme.
|
||||
///
|
||||
/// To override individual parts of the Material-derived Cupertino styling,
|
||||
/// `cupertinoOverrideTheme`'s construction parameters can be used.
|
||||
///
|
||||
/// To completely decouple the Cupertino styling from Material theme derivation,
|
||||
/// another [CupertinoTheme] widget can be inserted as a descendant of the
|
||||
/// Material [Theme]. On a [MaterialApp], this can be done using the `builder`
|
||||
/// parameter on the constructor.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoThemeData], whose null constructor parameters default to
|
||||
/// reasonable iOS styling defaults rather than harmonizing with a Material
|
||||
/// theme.
|
||||
/// * [Theme], widget which inserts a [CupertinoTheme] with this
|
||||
/// [MaterialBasedCupertinoThemeData].
|
||||
// This class subclasses CupertinoThemeData rather than composes one because it
|
||||
// _is_ a CupertinoThemeData with partially altered behavior. e.g. its textTheme
|
||||
// is from the superclass and based on the primaryColor but the primaryColor
|
||||
// comes from the Material theme unless overridden.
|
||||
class MaterialBasedCupertinoThemeData extends CupertinoThemeData {
|
||||
/// Create a [MaterialBasedCupertinoThemeData] based on a Material [ThemeData]
|
||||
/// and its `cupertinoOverrideTheme`.
|
||||
///
|
||||
/// The [materialTheme] parameter must not be null.
|
||||
MaterialBasedCupertinoThemeData({
|
||||
@required ThemeData materialTheme,
|
||||
}) : assert(materialTheme != null),
|
||||
_materialTheme = materialTheme,
|
||||
// Pass all values to the superclass so Material-agnostic properties
|
||||
// like barBackgroundColor can still behave like a normal
|
||||
// CupertinoThemeData.
|
||||
super.raw(
|
||||
materialTheme.cupertinoOverrideTheme?.brightness,
|
||||
materialTheme.cupertinoOverrideTheme?.primaryColor,
|
||||
materialTheme.cupertinoOverrideTheme?.primaryContrastingColor,
|
||||
materialTheme.cupertinoOverrideTheme?.textTheme,
|
||||
materialTheme.cupertinoOverrideTheme?.barBackgroundColor,
|
||||
materialTheme.cupertinoOverrideTheme?.scaffoldBackgroundColor,
|
||||
);
|
||||
|
||||
final ThemeData _materialTheme;
|
||||
|
||||
@override
|
||||
Brightness get brightness => _materialTheme.cupertinoOverrideTheme?.brightness ?? _materialTheme.brightness;
|
||||
|
||||
@override
|
||||
Color get primaryColor => _materialTheme.cupertinoOverrideTheme?.primaryColor ?? _materialTheme.colorScheme.primary;
|
||||
|
||||
@override
|
||||
Color get primaryContrastingColor => _materialTheme.cupertinoOverrideTheme?.primaryContrastingColor ?? _materialTheme.colorScheme.onPrimary;
|
||||
|
||||
@override
|
||||
Color get scaffoldBackgroundColor => _materialTheme.cupertinoOverrideTheme?.scaffoldBackgroundColor ?? _materialTheme.scaffoldBackgroundColor;
|
||||
|
||||
/// Copies the [ThemeData]'s `cupertinoOverrideTheme`.
|
||||
///
|
||||
/// Only the specified override attributes of the [ThemeData]'s
|
||||
/// `cupertinoOverrideTheme` and the newly specified parameters are in the
|
||||
/// returned [CupertinoThemeData]. No derived attributes from iOS defaults or
|
||||
/// from cascaded Material theme attributes are copied.
|
||||
///
|
||||
/// [MaterialBasedCupertinoThemeData.copyWith] cannot change the base
|
||||
/// Material [ThemeData]. To change the base Material [ThemeData], create a
|
||||
/// new Material [Theme] and use `copyWith` on the Material [ThemeData]
|
||||
/// instead.
|
||||
@override
|
||||
CupertinoThemeData copyWith({
|
||||
Brightness brightness,
|
||||
Color primaryColor,
|
||||
Color primaryContrastingColor,
|
||||
CupertinoTextThemeData textTheme,
|
||||
Color barBackgroundColor,
|
||||
Color scaffoldBackgroundColor,
|
||||
}) {
|
||||
return _materialTheme.cupertinoOverrideTheme?.copyWith(
|
||||
brightness: brightness,
|
||||
primaryColor: primaryColor,
|
||||
primaryContrastingColor: primaryContrastingColor,
|
||||
textTheme: textTheme,
|
||||
barBackgroundColor: barBackgroundColor,
|
||||
scaffoldBackgroundColor: scaffoldBackgroundColor,
|
||||
) ?? CupertinoThemeData(
|
||||
brightness: brightness,
|
||||
primaryColor: primaryColor,
|
||||
primaryContrastingColor: primaryContrastingColor,
|
||||
textTheme: textTheme,
|
||||
barBackgroundColor: barBackgroundColor,
|
||||
scaffoldBackgroundColor: scaffoldBackgroundColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
|
|||
// Proof by contradiction:
|
||||
//
|
||||
// If we introduce a loop, this retained layer must be appended to one of
|
||||
// its descendent layers, say A. That means the child structure of A has
|
||||
// its descendant layers, say A. That means the child structure of A has
|
||||
// changed so A's _needsAddToScene is true. This contradicts
|
||||
// _subtreeNeedsAddToScene being false.
|
||||
if (!_subtreeNeedsAddToScene && _engineLayer != null) {
|
||||
|
|
|
@ -46,7 +46,7 @@ import 'framework.dart';
|
|||
///
|
||||
/// When the inherited model is rebuilt the [updateShouldNotify] and
|
||||
/// [updateShouldNotifyDependent] methods are used to decide what
|
||||
/// should be rebuilt. If [updateShouldNotify] returns true, then the
|
||||
/// should be rebuilt. If [updateShouldNotify] returns true, then the
|
||||
/// inherited model's [updateShouldNotifyDependent] method is tested for
|
||||
/// each dependent and the set of aspect objects it depends on.
|
||||
/// The [updateShouldNotifyDependent] method must compare the set of aspect
|
||||
|
@ -153,6 +153,8 @@ abstract class InheritedModel<T> extends InheritedWidget {
|
|||
///
|
||||
/// If [aspect] is null this method is the same as
|
||||
/// `context.inheritFromWidgetOfExactType(T)`.
|
||||
///
|
||||
/// If no ancestor of type T exists, null is returned.
|
||||
static T inheritFrom<T extends InheritedModel<Object>>(BuildContext context, { Object aspect }) {
|
||||
if (aspect == null)
|
||||
return context.inheritFromWidgetOfExactType(T);
|
||||
|
@ -160,6 +162,10 @@ abstract class InheritedModel<T> extends InheritedWidget {
|
|||
// Create a dependency on all of the type T ancestor models up until
|
||||
// a model is found for which isSupportedAspect(aspect) is true.
|
||||
final List<InheritedElement> models = _findModels<T>(context, aspect).toList();
|
||||
if (models.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final InheritedElement lastModel = models.last;
|
||||
for (InheritedElement model in models) {
|
||||
final T value = context.inheritFromElement(model, aspect: aspect);
|
||||
|
|
|
@ -43,7 +43,7 @@ typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value, W
|
|||
/// * [AnimatedBuilder], which also triggers rebuilds from a [Listenable]
|
||||
/// without passing back a specific value from a [ValueListenable].
|
||||
/// * [NotificationListener], which lets you rebuild based on [Notification]
|
||||
/// coming from its descendent widgets rather than a [ValueListenable] that
|
||||
/// coming from its descendant widgets rather than a [ValueListenable] that
|
||||
/// you have a direct reference to.
|
||||
/// * [StreamBuilder], where a builder can depend on a [Stream] rather than
|
||||
/// a [ValueListenable] for more advanced use cases.
|
||||
|
|
|
@ -68,6 +68,70 @@ void main() {
|
|||
expect(actualActive.text.style.color, const Color(0xFF123456));
|
||||
});
|
||||
|
||||
testWidgets('Tabs respects themes', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoTabBar(
|
||||
items: const <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: ImageIcon(TestImageProvider(24, 24)),
|
||||
title: Text('Tab 1'),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: ImageIcon(TestImageProvider(24, 24)),
|
||||
title: Text('Tab 2'),
|
||||
),
|
||||
],
|
||||
currentIndex: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
RichText actualInactive = tester.widget(find.descendant(
|
||||
of: find.text('Tab 1'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
expect(actualInactive.text.style.color, CupertinoColors.inactiveGray);
|
||||
|
||||
RichText actualActive = tester.widget(find.descendant(
|
||||
of: find.text('Tab 2'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
expect(actualActive.text.style.color, CupertinoColors.activeBlue);
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: CupertinoTabBar(
|
||||
items: const <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: ImageIcon(TestImageProvider(24, 24)),
|
||||
title: Text('Tab 1'),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: ImageIcon(TestImageProvider(24, 24)),
|
||||
title: Text('Tab 2'),
|
||||
),
|
||||
],
|
||||
currentIndex: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
actualInactive = tester.widget(find.descendant(
|
||||
of: find.text('Tab 1'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
expect(actualInactive.text.style.color, CupertinoColors.inactiveGray);
|
||||
|
||||
actualActive = tester.widget(find.descendant(
|
||||
of: find.text('Tab 2'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
expect(actualActive.text.style.color, CupertinoColors.activeOrange);
|
||||
|
||||
});
|
||||
|
||||
testWidgets('Use active icon', (WidgetTester tester) async {
|
||||
const TestImageProvider activeIcon = TestImageProvider(16, 16);
|
||||
const TestImageProvider inactiveIcon = TestImageProvider(24, 24);
|
||||
|
|
|
@ -12,6 +12,7 @@ import '../widgets/semantics_tester.dart';
|
|||
const TextStyle testStyle = TextStyle(
|
||||
fontFamily: 'Ahem',
|
||||
fontSize: 10.0,
|
||||
letterSpacing: 0.0,
|
||||
);
|
||||
|
||||
void main() {
|
||||
|
@ -226,6 +227,80 @@ void main() {
|
|||
|
||||
expect(boxDecoration.color, const Color(0x00FF00));
|
||||
});
|
||||
|
||||
testWidgets('Botton respects themes', (WidgetTester tester) async {
|
||||
TextStyle textStyle;
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
textStyle = DefaultTextStyle.of(context).style;
|
||||
return const Placeholder();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(textStyle.color, CupertinoColors.activeBlue);
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoButton.filled(
|
||||
onPressed: () {},
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
textStyle = DefaultTextStyle.of(context).style;
|
||||
return const Placeholder();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(textStyle.color, CupertinoColors.white);
|
||||
BoxDecoration decoration = tester.widget<DecoratedBox>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoButton),
|
||||
matching: find.byType(DecoratedBox)
|
||||
)
|
||||
).decoration;
|
||||
expect(decoration.color, CupertinoColors.activeBlue);
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
textStyle = DefaultTextStyle.of(context).style;
|
||||
return const Placeholder();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(textStyle.color, CupertinoColors.activeOrange);
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: CupertinoButton.filled(
|
||||
onPressed: () {},
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
textStyle = DefaultTextStyle.of(context).style;
|
||||
return const Placeholder();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(textStyle.color, CupertinoColors.black);
|
||||
decoration = tester.widget<DecoratedBox>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoButton),
|
||||
matching: find.byType(DecoratedBox)
|
||||
)
|
||||
).decoration;
|
||||
expect(decoration.color, CupertinoColors.activeOrange);
|
||||
});
|
||||
}
|
||||
|
||||
Widget boilerplate({ Widget child }) {
|
||||
|
|
|
@ -148,15 +148,62 @@ void main() {
|
|||
expect(tester.getCenter(find.text('Title')).dx, 400.0);
|
||||
});
|
||||
|
||||
testWidgets('Verify styles of each slot', (WidgetTester tester) async {
|
||||
testWidgets('Nav bar uses theme defaults', (WidgetTester tester) async {
|
||||
count = 0x000000;
|
||||
await tester.pumpWidget(
|
||||
const CupertinoApp(
|
||||
CupertinoApp(
|
||||
home: CupertinoNavigationBar(
|
||||
leading: _ExpectStyles(color: Color(0xFF001122), index: 0x000001),
|
||||
middle: _ExpectStyles(color: Color(0xFF000000), letterSpacing: -0.08, index: 0x000100),
|
||||
trailing: _ExpectStyles(color: Color(0xFF001122), index: 0x010000),
|
||||
actionsForegroundColor: Color(0xFF001122),
|
||||
leading: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: CupertinoColors.activeBlue, index: 0x000001),
|
||||
),
|
||||
middle: const _ExpectStyles(color: CupertinoColors.black, index: 0x000100),
|
||||
trailing: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: CupertinoColors.activeBlue, index: 0x010000),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(count, 0x010101);
|
||||
});
|
||||
|
||||
testWidgets('Nav bar respects themes', (WidgetTester tester) async {
|
||||
count = 0x000000;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: CupertinoNavigationBar(
|
||||
leading: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: CupertinoColors.activeOrange, index: 0x000001),
|
||||
),
|
||||
middle: const _ExpectStyles(color: CupertinoColors.white, index: 0x000100),
|
||||
trailing: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: CupertinoColors.activeOrange, index: 0x010000),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(count, 0x010101);
|
||||
});
|
||||
|
||||
testWidgets('Theme active color can be overriden', (WidgetTester tester) async {
|
||||
count = 0x000000;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoNavigationBar(
|
||||
leading: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: Color(0xFF001122), index: 0x000001),
|
||||
),
|
||||
middle: const _ExpectStyles(color: Color(0xFF000000), index: 0x000100),
|
||||
trailing: CupertinoButton(
|
||||
onPressed: () {},
|
||||
child: const _ExpectStyles(color: Color(0xFF001122), index: 0x010000),
|
||||
),
|
||||
actionsForegroundColor: const Color(0xFF001122),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -845,18 +892,18 @@ void main() {
|
|||
}
|
||||
|
||||
class _ExpectStyles extends StatelessWidget {
|
||||
const _ExpectStyles({ this.color, this.letterSpacing, this.index });
|
||||
const _ExpectStyles({ this.color, this.index });
|
||||
|
||||
final Color color;
|
||||
final double letterSpacing;
|
||||
final int index;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextStyle style = DefaultTextStyle.of(context).style;
|
||||
expect(style.color, color);
|
||||
expect(style.fontFamily, '.SF Pro Text');
|
||||
expect(style.fontSize, 17.0);
|
||||
expect(style.letterSpacing, letterSpacing ?? -0.24);
|
||||
expect(style.letterSpacing, -0.41);
|
||||
count += index;
|
||||
return Container();
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ Future<void> startTransitionBetween(
|
|||
String fromTitle,
|
||||
String toTitle,
|
||||
TextDirection textDirection = TextDirection.ltr,
|
||||
CupertinoThemeData theme,
|
||||
}) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: theme,
|
||||
builder: (BuildContext context, Widget navigator) {
|
||||
return Directionality(
|
||||
textDirection: textDirection,
|
||||
|
@ -79,19 +81,15 @@ Finder flying(WidgetTester tester, Finder finder) {
|
|||
});
|
||||
|
||||
assert(
|
||||
find
|
||||
.descendant(
|
||||
of: lastOverlayFinder,
|
||||
matching: find.byWidgetPredicate(
|
||||
(Widget widget) =>
|
||||
widget.runtimeType.toString() ==
|
||||
'_NavigationBarTransition',
|
||||
),
|
||||
)
|
||||
.evaluate()
|
||||
.length ==
|
||||
1,
|
||||
'The last overlay in the navigator was not a flying hero',);
|
||||
find.descendant(
|
||||
of: lastOverlayFinder,
|
||||
matching: find.byWidgetPredicate(
|
||||
(Widget widget) =>
|
||||
widget.runtimeType.toString() == '_NavigationBarTransition',
|
||||
),
|
||||
).evaluate().length == 1,
|
||||
'The last overlay in the navigator was not a flying hero',
|
||||
);
|
||||
|
||||
return find.descendant(
|
||||
of: lastOverlayFinder,
|
||||
|
@ -103,28 +101,25 @@ void checkBackgroundBoxHeight(WidgetTester tester, double height) {
|
|||
final Widget transitionBackgroundBox =
|
||||
tester.widget<Stack>(flying(tester, find.byType(Stack))).children[0];
|
||||
expect(
|
||||
tester
|
||||
.widget<SizedBox>(
|
||||
find.descendant(
|
||||
of: find.byWidget(transitionBackgroundBox),
|
||||
matching: find.byType(SizedBox),
|
||||
),
|
||||
)
|
||||
.height,
|
||||
tester.widget<SizedBox>(
|
||||
find.descendant(
|
||||
of: find.byWidget(transitionBackgroundBox),
|
||||
matching: find.byType(SizedBox),
|
||||
),
|
||||
).height,
|
||||
height,
|
||||
);
|
||||
}
|
||||
|
||||
void checkOpacity(WidgetTester tester, Finder finder, double opacity) {
|
||||
expect(
|
||||
tester
|
||||
.renderObject<RenderAnimatedOpacity>(find.ancestor(
|
||||
of: finder,
|
||||
matching: find.byType(FadeTransition),
|
||||
))
|
||||
.opacity
|
||||
.value,
|
||||
opacity,
|
||||
tester.renderObject<RenderAnimatedOpacity>(
|
||||
find.ancestor(
|
||||
of: finder,
|
||||
matching: find.byType(FadeTransition),
|
||||
),
|
||||
).opacity.value,
|
||||
moreOrLessEquals(opacity, epsilon: 0.000000001),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -144,11 +139,11 @@ void main() {
|
|||
// place.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(331.0724935531616, 13.5),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(331.0724935531616, 13.5),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -167,11 +162,11 @@ void main() {
|
|||
// Same as LTR but more to the right now.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(366.9275064468384, 13.5),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(366.9275064468384, 13.5),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -187,8 +182,8 @@ void main() {
|
|||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w600);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF UI Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.08952957153320312);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(
|
||||
tester, flying(tester, find.text('Page 1')).first, 0.8609542846679688);
|
||||
|
@ -199,8 +194,8 @@ void main() {
|
|||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w600);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF UI Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.08952957153320312);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0);
|
||||
|
||||
|
@ -208,20 +203,69 @@ void main() {
|
|||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF0073F0));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF UI Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.231169798374176);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF0073F0));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF UI Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.231169798374176);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(
|
||||
tester, flying(tester, find.text('Page 1')).last, 0.8733493089675903);
|
||||
});
|
||||
|
||||
testWidgets('Font transitions respect themes',
|
||||
(WidgetTester tester) async {
|
||||
await startTransitionBetween(
|
||||
tester,
|
||||
fromTitle: 'Page 1',
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
);
|
||||
|
||||
// Be mid-transition.
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
|
||||
// The transition's stack is ordered. The bottom middle is inserted first.
|
||||
final RenderParagraph bottomMiddle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFFFFF8EF));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w600);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.8609542846679688);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
// are flipped.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFFFFF8EF));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w600);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0);
|
||||
|
||||
// Move animation further a bit.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFFFF9A0E));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFFFF9A0E));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.8733493089675903);
|
||||
});
|
||||
|
||||
testWidgets('Fullscreen dialogs do not create heroes',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
|
@ -295,7 +339,7 @@ void main() {
|
|||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(331.0724935531616, 13.5),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
|
@ -305,7 +349,7 @@ void main() {
|
|||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(331.0724935531616, 13.5),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -341,7 +385,7 @@ void main() {
|
|||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(366.9275064468384, 13.5),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
|
@ -351,7 +395,7 @@ void main() {
|
|||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(366.9275064468384, 13.5),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -731,14 +775,14 @@ void main() {
|
|||
checkOpacity(tester, flying(tester, find.text('custom')), 0.8393326997756958);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('custom'))),
|
||||
const Offset(683.0, 13.5),
|
||||
const Offset(684.0, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, flying(tester, find.text('custom')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('custom'))),
|
||||
const Offset(683.0, 13.5),
|
||||
const Offset(684.0, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -915,7 +959,7 @@ void main() {
|
|||
expect(bottomLargeTitle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomLargeTitle.text.style.fontWeight, FontWeight.w700);
|
||||
expect(bottomLargeTitle.text.style.fontFamily, '.SF Pro Display');
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, 0.21141128540039061);
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, 0.36116094589233394);
|
||||
|
||||
// The top back label is styled exactly the same way.
|
||||
final RenderParagraph topBackLabel =
|
||||
|
@ -923,19 +967,19 @@ void main() {
|
|||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w700);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Display');
|
||||
expect(topBackLabel.text.style.letterSpacing, 0.21141128540039061);
|
||||
expect(topBackLabel.text.style.letterSpacing, 0.36116094589233394);
|
||||
|
||||
// Move animation further a bit.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomLargeTitle.text.style.color, const Color(0xFF0073F0));
|
||||
expect(bottomLargeTitle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomLargeTitle.text.style.fontFamily, '.SF UI Text');
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, -0.2135093951225281);
|
||||
expect(bottomLargeTitle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, -0.3647452166676521);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF0073F0));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF UI Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.2135093951225281);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.3647452166676521);
|
||||
});
|
||||
|
||||
testWidgets('Top middle fades in and slides in from the right',
|
||||
|
@ -952,7 +996,7 @@ void main() {
|
|||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(725.1760711669922, 13.5),
|
||||
const Offset(726.1760711669922, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
@ -960,7 +1004,7 @@ void main() {
|
|||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6972532719373703);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(408.02137756347656, 13.5),
|
||||
const Offset(409.02137756347656, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -979,7 +1023,7 @@ void main() {
|
|||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(74.82392883300781, 13.5),
|
||||
const Offset(73.82392883300781, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
@ -987,7 +1031,7 @@ void main() {
|
|||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6972532719373703);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(391.97862243652344, 13.5),
|
||||
const Offset(390.97862243652344, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1115,7 +1159,7 @@ void main() {
|
|||
// Page 2, which is the middle of the top route, start to fly back to the right.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(352.5802058875561, 13.5),
|
||||
const Offset(353.5802058875561, 13.5),
|
||||
);
|
||||
|
||||
// Page 1 is in transition in 2 places. Once as the top back label and once
|
||||
|
@ -1131,12 +1175,12 @@ void main() {
|
|||
// Transition continues.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(654.2055835723877, 13.5),
|
||||
const Offset(655.2055835723877, 13.5),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(720.8727767467499, 13.5),
|
||||
const Offset(721.8727767467499, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
@ -1171,7 +1215,7 @@ void main() {
|
|||
// Page 2, which is the middle of the top route, start to fly back to the right.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(352.5802058875561, 13.5),
|
||||
const Offset(353.5802058875561, 13.5),
|
||||
);
|
||||
|
||||
await gesture.up();
|
||||
|
@ -1180,12 +1224,12 @@ void main() {
|
|||
// Transition continues from the point we let off.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(352.5802058875561, 13.5),
|
||||
const Offset(353.5802058875561, 13.5),
|
||||
);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(350.00985169410706, 13.5),
|
||||
const Offset(351.00985169410706, 13.5),
|
||||
);
|
||||
|
||||
// Finish the snap back animation.
|
||||
|
|
|
@ -142,7 +142,7 @@ void main() {
|
|||
}
|
||||
});
|
||||
|
||||
testWidgets('Children, onValueChanged, and color arguments can not be null',
|
||||
testWidgets('Children and onValueChanged arguments can not be null',
|
||||
(WidgetTester tester) async {
|
||||
try {
|
||||
await tester.pumpWidget(
|
||||
|
@ -175,21 +175,6 @@ void main() {
|
|||
} on AssertionError catch (e) {
|
||||
expect(e.toString(), contains('onValueChanged'));
|
||||
}
|
||||
|
||||
try {
|
||||
await tester.pumpWidget(
|
||||
boilerplate(
|
||||
child: CupertinoSegmentedControl<int>(
|
||||
children: children,
|
||||
onValueChanged: (int newValue) {},
|
||||
unselectedColor: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
fail('Should not be possible to create segmented control with null unselectedColor');
|
||||
} on AssertionError catch (e) {
|
||||
expect(e.toString(), contains('unselectedColor'));
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('Widgets have correct default text/icon styles, change correctly on selection',
|
||||
|
@ -236,6 +221,52 @@ void main() {
|
|||
expect(iconTheme.data.color, CupertinoColors.white);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Segmented controls respects themes',
|
||||
(WidgetTester tester) async {
|
||||
final Map<int, Widget> children = <int, Widget>{};
|
||||
children[0] = const Text('Child 1');
|
||||
children[1] = const Icon(IconData(1));
|
||||
|
||||
int sharedValue = 0;
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return CupertinoSegmentedControl<int>(
|
||||
children: children,
|
||||
onValueChanged: (int newValue) {
|
||||
setState(() {
|
||||
sharedValue = newValue;
|
||||
});
|
||||
},
|
||||
groupValue: sharedValue,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
DefaultTextStyle textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
|
||||
IconTheme iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1)));
|
||||
|
||||
expect(textStyle.style.color, CupertinoColors.black);
|
||||
expect(iconTheme.data.color, CupertinoColors.activeOrange);
|
||||
|
||||
await tester.tap(find.widgetWithIcon(IconTheme, const IconData(1)));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
textStyle = tester.widget(find.widgetWithText(DefaultTextStyle, 'Child 1').first);
|
||||
iconTheme = tester.widget(find.widgetWithIcon(IconTheme, const IconData(1)));
|
||||
|
||||
expect(textStyle.style.color, CupertinoColors.activeOrange);
|
||||
expect(iconTheme.data.color, CupertinoColors.black);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('SegmentedControl is correct when user provides custom colors',
|
||||
(WidgetTester tester) async {
|
||||
final Map<int, Widget> children = <int, Widget>{};
|
||||
|
@ -1171,13 +1202,13 @@ void main() {
|
|||
await tester.tap(find.text('B'));
|
||||
|
||||
await tester.pump();
|
||||
expect(getBackgroundColor(tester, 0), const Color(0xff007aff));
|
||||
expect(getBackgroundColor(tester, 1), const Color(0x33007aff));
|
||||
expect(getBackgroundColor(tester, 0), CupertinoColors.white);
|
||||
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
|
||||
expect(getBackgroundColor(tester, 3), CupertinoColors.white);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 40));
|
||||
expect(getBackgroundColor(tester, 0), const Color(0xff3d9aff));
|
||||
expect(getBackgroundColor(tester, 1), const Color(0x64007aff));
|
||||
expect(getBackgroundColor(tester, 0), CupertinoColors.white);
|
||||
expect(getBackgroundColor(tester, 1), CupertinoColors.activeBlue);
|
||||
expect(getBackgroundColor(tester, 3), CupertinoColors.white);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart';
|
|||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -359,4 +360,57 @@ void main() {
|
|||
|
||||
handle.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Slider respects themes', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoSlider(
|
||||
onChanged: (double value) {},
|
||||
value: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
find.byType(CupertinoSlider),
|
||||
// First line it paints is blue.
|
||||
paints..rrect(color: CupertinoColors.activeBlue),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: Center(
|
||||
child: CupertinoSlider(
|
||||
onChanged: (double value) {},
|
||||
value: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
find.byType(CupertinoSlider),
|
||||
paints..rrect(color: CupertinoColors.activeOrange),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Themes can be overridden', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
||||
home: Center(
|
||||
child: CupertinoSlider(
|
||||
activeColor: CupertinoColors.activeGreen,
|
||||
onChanged: (double value) {},
|
||||
value: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
find.byType(CupertinoSlider),
|
||||
paints..rrect(color: CupertinoColors.activeGreen),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -254,6 +254,64 @@ void main() {
|
|||
expect(tabsPainted, <int>[0, 1, 0]);
|
||||
expect(selectedTabs, <int>[0]);
|
||||
});
|
||||
|
||||
testWidgets('Tab bar respects themes', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: CupertinoTabScaffold(
|
||||
tabBar: _buildTabBar(),
|
||||
tabBuilder: (BuildContext context, int index) {
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
BoxDecoration tabDecoration = tester.widget<DecoratedBox>(find.descendant(
|
||||
of: find.byType(CupertinoTabBar),
|
||||
matching: find.byType(DecoratedBox),
|
||||
)).decoration;
|
||||
|
||||
expect(tabDecoration.color, const Color(0xCCF8F8F8));
|
||||
|
||||
await tester.tap(find.text('Tab 2'));
|
||||
await tester.pump();
|
||||
|
||||
// Pump again but with dark theme.
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
theme: const CupertinoThemeData(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: CupertinoColors.destructiveRed,
|
||||
),
|
||||
home: CupertinoTabScaffold(
|
||||
tabBar: _buildTabBar(),
|
||||
tabBuilder: (BuildContext context, int index) {
|
||||
return const Placeholder();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
tabDecoration = tester.widget<DecoratedBox>(find.descendant(
|
||||
of: find.byType(CupertinoTabBar),
|
||||
matching: find.byType(DecoratedBox),
|
||||
)).decoration;
|
||||
|
||||
expect(tabDecoration.color, const Color(0xB7212121));
|
||||
|
||||
final RichText tab1 = tester.widget(find.descendant(
|
||||
of: find.text('Tab 1'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
// Tab 2 should still be selected after changing theme.
|
||||
expect(tab1.text.style.color, CupertinoColors.inactiveGray);
|
||||
final RichText tab2 = tester.widget(find.descendant(
|
||||
of: find.text('Tab 2'),
|
||||
matching: find.byType(RichText),
|
||||
));
|
||||
expect(tab2.text.style.color, CupertinoColors.destructiveRed);
|
||||
});
|
||||
}
|
||||
|
||||
CupertinoTabBar _buildTabBar({ int selectedTab = 0 }) {
|
||||
|
@ -268,7 +326,6 @@ CupertinoTabBar _buildTabBar({ int selectedTab = 0 }) {
|
|||
title: Text('Tab 2'),
|
||||
),
|
||||
],
|
||||
backgroundColor: CupertinoColors.white,
|
||||
currentIndex: selectedTab,
|
||||
onTap: (int newTab) => selectedTabs.add(newTab),
|
||||
);
|
||||
|
|
|
@ -1098,4 +1098,42 @@ void main() {
|
|||
expect(find.byType(CupertinoButton), findsNWidgets(3));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'text field respects theme',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const CupertinoApp(
|
||||
theme: CupertinoThemeData(
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
home: Center(
|
||||
child: CupertinoTextField(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final BoxDecoration decoration = tester.widget<DecoratedBox>(
|
||||
find.descendant(
|
||||
of: find.byType(CupertinoTextField),
|
||||
matching: find.byType(DecoratedBox)
|
||||
),
|
||||
).decoration;
|
||||
|
||||
expect(
|
||||
decoration.border.bottom.color,
|
||||
CupertinoColors.lightBackgroundGray, // Border color is the same regardless.
|
||||
);
|
||||
|
||||
await tester.enterText(find.byType(CupertinoTextField), 'smoked meat');
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
tester.renderObject<RenderEditable>(
|
||||
find.byElementPredicate((Element element) => element.renderObject is RenderEditable)
|
||||
).text.style.color,
|
||||
CupertinoColors.white,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
126
packages/flutter/test/cupertino/theme_test.dart
Normal file
126
packages/flutter/test/cupertino/theme_test.dart
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
int buildCount;
|
||||
CupertinoThemeData actualTheme;
|
||||
|
||||
final Widget singletonThemeSubtree = Builder(
|
||||
builder: (BuildContext context) {
|
||||
buildCount++;
|
||||
actualTheme = CupertinoTheme.of(context);
|
||||
return const Placeholder();
|
||||
},
|
||||
);
|
||||
|
||||
Future<CupertinoThemeData> testTheme(WidgetTester tester, CupertinoThemeData theme) async {
|
||||
await tester.pumpWidget(
|
||||
CupertinoTheme(
|
||||
data: theme,
|
||||
child: singletonThemeSubtree,
|
||||
),
|
||||
);
|
||||
return actualTheme;
|
||||
}
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
buildCount = 0;
|
||||
actualTheme = null;
|
||||
});
|
||||
|
||||
testWidgets('Default theme has defaults', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, const CupertinoThemeData());
|
||||
|
||||
expect(theme.brightness, Brightness.light);
|
||||
expect(theme.primaryColor, CupertinoColors.activeBlue);
|
||||
expect(theme.textTheme.textStyle.fontSize, 17.0);
|
||||
});
|
||||
|
||||
testWidgets('Theme attributes cascade', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, const CupertinoThemeData(
|
||||
primaryColor: CupertinoColors.destructiveRed,
|
||||
));
|
||||
|
||||
expect(theme.textTheme.actionTextStyle.color, CupertinoColors.destructiveRed);
|
||||
});
|
||||
|
||||
testWidgets('Dependent attribute can be overridden from cascaded value', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, const CupertinoThemeData(
|
||||
brightness: Brightness.dark,
|
||||
textTheme: CupertinoTextThemeData(
|
||||
textStyle: TextStyle(color: CupertinoColors.black),
|
||||
)
|
||||
));
|
||||
|
||||
// The brightness still cascaded down to the background color.
|
||||
expect(theme.scaffoldBackgroundColor, CupertinoColors.black);
|
||||
// But not to the font color which we overrode.
|
||||
expect(theme.textTheme.textStyle.color, CupertinoColors.black);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Reading themes creates dependencies',
|
||||
(WidgetTester tester) async {
|
||||
// Reading the theme creates a dependency.
|
||||
CupertinoThemeData theme = await testTheme(tester, const CupertinoThemeData(
|
||||
// Default brightness is light,
|
||||
barBackgroundColor: Color(0x11223344),
|
||||
textTheme: CupertinoTextThemeData(
|
||||
textStyle: TextStyle(fontFamily: 'Skeuomorphic'),
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 1);
|
||||
expect(theme.textTheme.textStyle.fontFamily, 'Skeuomorphic');
|
||||
|
||||
// Changing another property also triggers a rebuild.
|
||||
theme = await testTheme(tester, const CupertinoThemeData(
|
||||
brightness: Brightness.light,
|
||||
barBackgroundColor: Color(0x11223344),
|
||||
textTheme: CupertinoTextThemeData(
|
||||
textStyle: TextStyle(fontFamily: 'Skeuomorphic'),
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 2);
|
||||
// Re-reading the same value doesn't change anything.
|
||||
expect(theme.textTheme.textStyle.fontFamily, 'Skeuomorphic');
|
||||
|
||||
theme = await testTheme(tester, const CupertinoThemeData(
|
||||
brightness: Brightness.light,
|
||||
barBackgroundColor: Color(0x11223344),
|
||||
textTheme: CupertinoTextThemeData(
|
||||
textStyle: TextStyle(fontFamily: 'Flat'),
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 3);
|
||||
expect(theme.textTheme.textStyle.fontFamily, 'Flat');
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'copyWith works',
|
||||
(WidgetTester tester) async {
|
||||
const CupertinoThemeData originalTheme = CupertinoThemeData(
|
||||
brightness: Brightness.dark,
|
||||
);
|
||||
|
||||
final CupertinoThemeData theme = await testTheme(tester, originalTheme.copyWith(
|
||||
primaryColor: CupertinoColors.activeGreen,
|
||||
));
|
||||
|
||||
expect(theme.brightness, Brightness.dark);
|
||||
expect(theme.primaryColor, CupertinoColors.activeGreen);
|
||||
// Now check calculated derivatives.
|
||||
expect(theme.textTheme.actionTextStyle.color, CupertinoColors.activeGreen);
|
||||
expect(theme.scaffoldBackgroundColor, CupertinoColors.black);
|
||||
},
|
||||
);
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/src/foundation/diagnostics.dart';
|
||||
|
@ -412,6 +413,227 @@ void main() {
|
|||
|
||||
expect(theme.textTheme.display4.debugLabel, '(englishLike display4 2014).merge(blackMountainView display4)');
|
||||
});
|
||||
|
||||
group('Cupertino theme', () {
|
||||
int buildCount;
|
||||
CupertinoThemeData actualTheme;
|
||||
|
||||
final Widget singletonThemeSubtree = Builder(
|
||||
builder: (BuildContext context) {
|
||||
buildCount++;
|
||||
actualTheme = CupertinoTheme.of(context);
|
||||
return const Placeholder();
|
||||
},
|
||||
);
|
||||
|
||||
Future<CupertinoThemeData> testTheme(WidgetTester tester, ThemeData theme) async {
|
||||
await tester.pumpWidget(
|
||||
Theme(
|
||||
data: theme,
|
||||
child: singletonThemeSubtree,
|
||||
),
|
||||
);
|
||||
return actualTheme;
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
buildCount = 0;
|
||||
actualTheme = null;
|
||||
});
|
||||
|
||||
testWidgets('Default theme has defaults', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData.light());
|
||||
|
||||
expect(theme.brightness, Brightness.light);
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
expect(theme.scaffoldBackgroundColor, Colors.grey[50]);
|
||||
expect(theme.primaryContrastingColor, Colors.white);
|
||||
expect(theme.textTheme.textStyle.fontFamily, '.SF Pro Text');
|
||||
expect(theme.textTheme.textStyle.fontSize, 17.0);
|
||||
});
|
||||
|
||||
testWidgets('Dark theme has defaults', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData.dark());
|
||||
|
||||
expect(theme.brightness, Brightness.dark);
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
expect(theme.primaryContrastingColor, Colors.white);
|
||||
expect(theme.scaffoldBackgroundColor, Colors.grey[850]);
|
||||
expect(theme.textTheme.textStyle.fontFamily, '.SF Pro Text');
|
||||
expect(theme.textTheme.textStyle.fontSize, 17.0);
|
||||
});
|
||||
|
||||
testWidgets('Can override material theme', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
scaffoldBackgroundColor: CupertinoColors.lightBackgroundGray,
|
||||
),
|
||||
));
|
||||
|
||||
expect(theme.brightness, Brightness.light);
|
||||
// We took the scaffold background override but the rest are still cascaded
|
||||
// to the material theme.
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
expect(theme.primaryContrastingColor, Colors.white);
|
||||
expect(theme.scaffoldBackgroundColor, CupertinoColors.lightBackgroundGray);
|
||||
expect(theme.textTheme.textStyle.fontFamily, '.SF Pro Text');
|
||||
expect(theme.textTheme.textStyle.fontSize, 17.0);
|
||||
});
|
||||
|
||||
testWidgets('Can override properties that are independent of material', (WidgetTester tester) async {
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
// The bar colors ignore all things material except brightness.
|
||||
barBackgroundColor: CupertinoColors.black,
|
||||
),
|
||||
));
|
||||
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
// MaterialBasedCupertinoThemeData should also function like a normal CupertinoThemeData.
|
||||
expect(theme.barBackgroundColor, CupertinoColors.black);
|
||||
});
|
||||
|
||||
testWidgets('Changing material theme triggers rebuilds', (WidgetTester tester) async {
|
||||
CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.red,
|
||||
));
|
||||
|
||||
expect(buildCount, 1);
|
||||
expect(theme.primaryColor, Colors.red);
|
||||
|
||||
theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.orange,
|
||||
));
|
||||
|
||||
expect(buildCount, 2);
|
||||
expect(theme.primaryColor, Colors.orange);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Changing cupertino theme override triggers rebuilds',
|
||||
(WidgetTester tester) async {
|
||||
CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryColor: CupertinoColors.activeOrange,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 1);
|
||||
expect(theme.primaryColor, CupertinoColors.activeOrange);
|
||||
|
||||
theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryColor: CupertinoColors.activeGreen,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 2);
|
||||
expect(theme.primaryColor, CupertinoColors.activeGreen);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Cupertino theme override blocks derivative changes',
|
||||
(WidgetTester tester) async {
|
||||
CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryColor: CupertinoColors.activeOrange,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 1);
|
||||
expect(theme.primaryColor, CupertinoColors.activeOrange);
|
||||
|
||||
// Change the upstream material primary color.
|
||||
theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
// But the primary material color is preempted by the override.
|
||||
primaryColor: CupertinoColors.activeOrange,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 2);
|
||||
expect(theme.primaryColor, CupertinoColors.activeOrange);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Cupertino overrides do not block derivatives triggering rebuilds when derivatives are not overridden',
|
||||
(WidgetTester tester) async {
|
||||
CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryContrastingColor: CupertinoColors.destructiveRed,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 1);
|
||||
expect(theme.textTheme.actionTextStyle.color, Colors.purple);
|
||||
expect(theme.primaryContrastingColor, CupertinoColors.destructiveRed);
|
||||
|
||||
theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.green,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryContrastingColor: CupertinoColors.destructiveRed,
|
||||
),
|
||||
));
|
||||
|
||||
expect(buildCount, 2);
|
||||
expect(theme.textTheme.actionTextStyle.color, Colors.green);
|
||||
expect(theme.primaryContrastingColor, CupertinoColors.destructiveRed);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'copyWith only copies the overrides, not the material or cupertino derivatives',
|
||||
(WidgetTester tester) async {
|
||||
final CupertinoThemeData originalTheme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
cupertinoOverrideTheme: const CupertinoThemeData(
|
||||
primaryContrastingColor: CupertinoColors.activeOrange,
|
||||
),
|
||||
));
|
||||
|
||||
final CupertinoThemeData copiedTheme = originalTheme.copyWith(
|
||||
barBackgroundColor: CupertinoColors.destructiveRed,
|
||||
);
|
||||
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
cupertinoOverrideTheme: copiedTheme,
|
||||
));
|
||||
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
expect(theme.primaryContrastingColor, CupertinoColors.activeOrange);
|
||||
expect(theme.barBackgroundColor, CupertinoColors.destructiveRed);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
"Material themes with no cupertino overrides can also be copyWith'ed",
|
||||
(WidgetTester tester) async {
|
||||
final CupertinoThemeData originalTheme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.purple,
|
||||
));
|
||||
|
||||
final CupertinoThemeData copiedTheme = originalTheme.copyWith(
|
||||
primaryContrastingColor: CupertinoColors.destructiveRed,
|
||||
);
|
||||
|
||||
final CupertinoThemeData theme = await testTheme(tester, ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
cupertinoOverrideTheme: copiedTheme,
|
||||
));
|
||||
|
||||
expect(theme.primaryColor, Colors.blue);
|
||||
expect(theme.primaryContrastingColor, CupertinoColors.destructiveRed);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
int testBuildCalled;
|
||||
|
|
|
@ -190,6 +190,22 @@ void main() {
|
|||
expect(find.text('a: 2 b: 2 c: 3'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Looking up an non existent InherintedModel ancestor returns null', (WidgetTester tester) async {
|
||||
ABCModel inheritedModel;
|
||||
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
inheritedModel = InheritedModel.inheritFrom(context);
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
// Shouldn't crash first of all.
|
||||
|
||||
expect(inheritedModel, null);
|
||||
});
|
||||
|
||||
testWidgets('Inner InheritedModel shadows the outer one', (WidgetTester tester) async {
|
||||
int _a = 0;
|
||||
int _b = 1;
|
||||
|
|
Loading…
Reference in a new issue