Unverified Commit feadfd2e authored by xster's avatar xster Committed by GitHub

Cupertino pull to refresh part 1: sliver and a simple indicator widget builder (#15324)

* Gallery scaffolding

* Started RenderSliver

* demo and initial hookup

* Cleaned up demo more and scaffolding basic sliver->widget communication structure.

* works

* states and default indicator building works

* start adding docs

* added an alignment setting optimized the sliver relayout mechanism

* tested a default bottom aligned sized indicator

* Added a bunch of tests

* more fixes and more tests

* Finished the tests

* Add docs

* Add more doc diffing wrt material pull to refresh

* Mention nav bar synergy

* add more asserts

* review 1

* Fix mockito 2 / dart 2 / strong typed tests

* review

* Remove the vscode config

* review
parent fcf09414
......@@ -7,5 +7,6 @@ export 'cupertino_buttons_demo.dart';
export 'cupertino_dialog_demo.dart';
export 'cupertino_navigation_demo.dart';
export 'cupertino_picker_demo.dart';
export 'cupertino_refresh_demo.dart';
export 'cupertino_slider_demo.dart';
export 'cupertino_switch_demo.dart';
// 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 'dart:math' show Random;
import 'package:flutter/cupertino.dart';
class CupertinoRefreshControlDemo extends StatefulWidget {
static const String routeName = '/cupertino/refresh';
@override
_CupertinoRefreshControlDemoState createState() => new _CupertinoRefreshControlDemoState();
}
class _CupertinoRefreshControlDemoState extends State<CupertinoRefreshControlDemo> {
List<List<String>> randomizedContacts;
@override
void initState() {
super.initState();
repopulateList();
}
void repopulateList() {
final Random random = new Random();
randomizedContacts = new List<List<String>>.generate(
100,
(int index) {
return contacts[random.nextInt(contacts.length)]
// Randomly adds a telephone icon next to the contact or not.
..add(random.nextBool().toString());
}
);
}
@override
Widget build(BuildContext context) {
return new DefaultTextStyle(
style: const TextStyle(
fontFamily: '.SF UI Text',
inherit: false,
fontSize: 17.0,
color: CupertinoColors.black,
),
child: new CupertinoPageScaffold(
child: new DecoratedBox(
decoration: const BoxDecoration(color: const Color(0xFFEFEFF4)),
child: new CustomScrollView(
slivers: <Widget>[
const CupertinoSliverNavigationBar(
largeTitle: const Text('Cupertino Refresh'),
),
new CupertinoRefreshControl(
onRefresh: () {
return new Future<void>.delayed(const Duration(seconds: 2))
..then((_) => setState(() => repopulateList()));
},
),
new SliverSafeArea(
top: false, // Top safe area is consumed by the navigation bar.
sliver: new SliverList(
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
return new _ListItem(
name: randomizedContacts[index][0],
place: randomizedContacts[index][1],
date: randomizedContacts[index][2],
called: randomizedContacts[index][3] == 'true',
);
},
childCount: 20,
),
),
),
],
),
),
),
);
}
}
List<List<String>> contacts = <List<String>>[
<String>['George Washington', 'Westmoreland County', ' 4/30/1789'],
<String>['John Adams', 'Braintree', ' 3/4/1797'],
<String>['Thomas Jefferson', 'Shadwell', ' 3/4/1801'],
<String>['James Madison', 'Port Conway', ' 3/4/1809'],
<String>['James Monroe', 'Monroe Hall', ' 3/4/1817'],
<String>['Andrew Jackson', 'Waxhaws Region South/North', ' 3/4/1829'],
<String>['John Quincy Adams', 'Braintree', ' 3/4/1825'],
<String>['William Henry Harrison', 'Charles City County', ' 3/4/1841'],
<String>['Martin Van Buren', 'Kinderhook New', ' 3/4/1837'],
<String>['Zachary Taylor', 'Barboursville', ' 3/4/1849'],
<String>['John Tyler', 'Charles City County', ' 4/4/1841'],
<String>['James Buchanan', 'Cove Gap', ' 3/4/1857'],
<String>['James K. Polk', 'Pineville North', ' 3/4/1845'],
<String>['Millard Fillmore', 'Summerhill New', '7/9/1850'],
<String>['Franklin Pierce', 'Hillsborough New', ' 3/4/1853'],
<String>['Andrew Johnson', 'Raleigh North', ' 4/15/1865'],
<String>['Abraham Lincoln', 'Sinking Spring', ' 3/4/1861'],
<String>['Ulysses S. Grant', 'Point Pleasant', ' 3/4/1869'],
<String>['Rutherford B. Hayes', 'Delaware', ' 3/4/1877'],
<String>['Chester A. Arthur', 'Fairfield', ' 9/19/1881'],
<String>['James A. Garfield', 'Moreland Hills', ' 3/4/1881'],
<String>['Benjamin Harrison', 'North Bend', ' 3/4/1889'],
<String>['Grover Cleveland', 'Caldwell New', ' 3/4/1885'],
<String>['William McKinley', 'Niles', ' 3/4/1897'],
<String>['Woodrow Wilson', 'Staunton', ' 3/4/1913'],
<String>['William H. Taft', 'Cincinnati', ' 3/4/1909'],
<String>['Theodore Roosevelt', 'New York City New', ' 9/14/1901'],
<String>['Warren G. Harding', 'Blooming Grove', ' 3/4/1921'],
<String>['Calvin Coolidge', 'Plymouth', '8/2/1923'],
<String>['Herbert Hoover', 'West Branch', ' 3/4/1929'],
<String>['Franklin D. Roosevelt', 'Hyde Park New', ' 3/4/1933'],
<String>['Harry S. Truman', 'Lamar', ' 4/12/1945'],
<String>['Dwight D. Eisenhower', 'Denison', ' 1/20/1953'],
<String>['Lyndon B. Johnson', 'Stonewall', '11/22/1963'],
<String>['Ronald Reagan', 'Tampico', ' 1/20/1981'],
<String>['Richard Nixon', 'Yorba Linda', ' 1/20/1969'],
<String>['Gerald Ford', 'Omaha', 'August 9/1974'],
<String>['John F. Kennedy', 'Brookline', ' 1/20/1961'],
<String>['George H. W. Bush', 'Milton', ' 1/20/1989'],
<String>['Jimmy Carter', 'Plains', ' 1/20/1977'],
<String>['George W. Bush', 'New Haven', ' 1/20, 2001'],
<String>['Bill Clinton', 'Hope', ' 1/20/1993'],
<String>['Barack Obama', 'Honolulu', ' 1/20/2009'],
<String>['Donald J. Trump', 'New York City', ' 1/20/2017'],
];
class _ListItem extends StatelessWidget {
const _ListItem({
this.name,
this.place,
this.date,
this.called,
});
final String name;
final String place;
final String date;
final bool called;
@override
Widget build(BuildContext context) {
return new Container(
color: CupertinoColors.white,
height: 60.0,
padding: const EdgeInsets.only(top: 9.0),
child: new Row(
children: <Widget>[
new Container(
width: 38.0,
child: called
? new Align(
alignment: Alignment.topCenter,
child: new Icon(
CupertinoIcons.phone_solid,
color: CupertinoColors.inactiveGray,
size: 18.0,
),
)
: null,
),
new Expanded(
child: new Container(
decoration: const BoxDecoration(
border: const Border(
bottom: const BorderSide(color: const Color(0xFFBCBBC1), width: 0.0),
),
),
padding: const EdgeInsets.only(left: 1.0, bottom: 9.0, right: 10.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(
name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.w600,
letterSpacing: -0.41,
),
),
new Text(
place,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 15.0,
letterSpacing: -0.24,
color: CupertinoColors.inactiveGray,
),
),
],
),
),
new Text(
date,
style: const TextStyle(
color: CupertinoColors.inactiveGray,
fontSize: 15.0,
letterSpacing: -0.41,
),
),
new Padding(
padding: const EdgeInsets.only(left: 9.0),
child: new Icon(
CupertinoIcons.info,
color: CupertinoColors.activeBlue
),
),
],
),
),
),
],
),
);
}
}
......@@ -327,6 +327,13 @@ List<GalleryItem> _buildGalleryItems() {
routeName: CupertinoPickerDemo.routeName,
buildRoute: (BuildContext context) => new CupertinoPickerDemo(),
),
new GalleryItem(
title: 'Pull to refresh',
subtitle: 'Cupertino styled refresh controls',
category: 'Cupertino Components',
routeName: CupertinoRefreshControlDemo.routeName,
buildRoute: (BuildContext context) => new CupertinoRefreshControlDemo(),
),
new GalleryItem(
title: 'Sliders',
subtitle: 'Cupertino styled sliders',
......
......@@ -103,9 +103,7 @@ Future<Null> smokeDemo(WidgetTester tester, String routeName) async {
await tester.pump(const Duration(milliseconds: 400));
// Go back
final Finder backButton = find.byTooltip('Back');
expect(backButton, findsOneWidget);
await tester.tap(backButton);
await tester.pageBack();
await tester.pump(); // Start the pop "back" operation.
await tester.pump(); // Complete the willPop() Future.
await tester.pump(const Duration(milliseconds: 400)); // Wait until it has finished.
......
......@@ -72,6 +72,7 @@ const List<Demo> demos = const <Demo>[
const Demo('Dialogs'),
const Demo('Navigation'),
const Demo('Pickers'),
const Demo('Pull to refresh'),
const Demo('Sliders'),
const Demo('Switches'),
......
{
"version": "0.1.0",
"command": "flutter",
"args": [],
"showOutput": "always",
"echoCommand": true,
"tasks": [
{
// Assign key binding to workbench.action.tasks.test to quickly run
// the currently open test.
"taskName": "test",
"isTestCommand": true,
"isShellCommand": true,
"args": ["${file}"]
}
]
}
......@@ -16,6 +16,7 @@ export 'src/cupertino/icons.dart';
export 'src/cupertino/nav_bar.dart';
export 'src/cupertino/page_scaffold.dart';
export 'src/cupertino/picker.dart';
export 'src/cupertino/refresh.dart';
export 'src/cupertino/route.dart';
export 'src/cupertino/scrollbar.dart';
export 'src/cupertino/slider.dart';
......
......@@ -8,6 +8,8 @@ import 'package:flutter/widgets.dart';
import 'colors.dart';
const double _kDefaultIndicatorRadius = 10.0;
/// An iOS-style activity indicator.
///
/// See also:
......@@ -18,7 +20,10 @@ class CupertinoActivityIndicator extends StatefulWidget {
const CupertinoActivityIndicator({
Key key,
this.animating: true,
this.radius: _kDefaultIndicatorRadius,
}) : assert(animating != null),
assert(radius != null),
assert(radius > 0),
super(key: key);
/// Whether the activity indicator is running its animation.
......@@ -26,12 +31,15 @@ class CupertinoActivityIndicator extends StatefulWidget {
/// Defaults to true.
final bool animating;
/// Radius of the spinner widget.
///
/// Defaults to 10px. Must be positive and cannot be null.
final double radius;
@override
_CupertinoActivityIndicatorState createState() => new _CupertinoActivityIndicatorState();
}
const double _kIndicatorWidth = 20.0;
const double _kIndicatorHeight = 20.0;
class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator> with SingleTickerProviderStateMixin {
AnimationController _controller;
......@@ -68,11 +76,12 @@ class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
@override
Widget build(BuildContext context) {
return new SizedBox(
width: _kIndicatorWidth,
height: _kIndicatorHeight,
height: widget.radius * 2,
width: widget.radius * 2,
child: new CustomPaint(
painter: new _CupertinoActivityIndicatorPainter(
position: _controller,
radius: widget.radius,
),
),
);
......@@ -84,14 +93,23 @@ const int _kTickCount = 12;
const int _kHalfTickCount = _kTickCount ~/ 2;
const Color _kTickColor = CupertinoColors.lightBackgroundGray;
const Color _kActiveTickColor = const Color(0xFF9D9D9D);
final RRect _kTickFundamentalRRect = new RRect.fromLTRBXY(-10.0, 1.0, -5.0, -1.0, 1.0, 1.0);
class _CupertinoActivityIndicatorPainter extends CustomPainter {
_CupertinoActivityIndicatorPainter({
this.position,
}) : super(repaint: position);
double radius,
}) : tickFundamentalRRect = new RRect.fromLTRBXY(
-radius,
1.0 * radius / _kDefaultIndicatorRadius,
-radius / 2.0,
-1.0 * radius / _kDefaultIndicatorRadius,
1.0,
1.0
),
super(repaint: position);
final Animation<double> position;
final RRect tickFundamentalRRect;
@override
void paint(Canvas canvas, Size size) {
......@@ -105,7 +123,7 @@ class _CupertinoActivityIndicatorPainter extends CustomPainter {
for (int i = 0; i < _kTickCount; ++ i) {
final double t = (((i + activeTick) % _kTickCount) / _kHalfTickCount).clamp(0.0, 1.0);
paint.color = Color.lerp(_kActiveTickColor, _kTickColor, t);
canvas.drawRRect(_kTickFundamentalRRect, paint);
canvas.drawRRect(tickFundamentalRRect, paint);
canvas.rotate(-_kTwoPI / _kTickCount);
}
......
......@@ -90,4 +90,13 @@ class CupertinoIcons {
/// Three solid dots.
static const IconData ellipsis = const IconData(0xf46a, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A phone handset outline.
static const IconData phone = const IconData(0xf4b8, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A phone handset.
static const IconData phone_solid = const IconData(0xf4b9, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A solid down arrow.
static const IconData down_arrow = const IconData(0xf35d, fontFamily: iconFont, fontPackage: iconFontPackage);
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment