Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0c40945a
Unverified
Commit
0c40945a
authored
Jul 19, 2022
by
Marcel Čampa
Committed by
GitHub
Jul 19, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement `CupertinoListSection` and `CupertinoListTile` (#78732)
parent
9d2f5754
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
1871 additions
and
133 deletions
+1871
-133
list_section_base.0.dart
...s/api/lib/cupertino/list_section/list_section_base.0.dart
+95
-0
list_section_inset.0.dart
.../api/lib/cupertino/list_section/list_section_inset.0.dart
+95
-0
list_section_base.0_test.dart
...test/cupertino/list_section/list_section_base.0_test.dart
+29
-0
list_section_inset.0_test.dart
...est/cupertino/list_section/list_section_inset.0_test.dart
+29
-0
cupertino.dart
packages/flutter/lib/cupertino.dart
+2
-0
form_section.dart
packages/flutter/lib/src/cupertino/form_section.dart
+57
-130
list_section.dart
packages/flutter/lib/src/cupertino/list_section.dart
+486
-0
list_tile.dart
packages/flutter/lib/src/cupertino/list_tile.dart
+414
-0
form_section_test.dart
packages/flutter/test/cupertino/form_section_test.dart
+3
-3
list_section_test.dart
packages/flutter/test/cupertino/list_section_test.dart
+179
-0
list_tile_test.dart
packages/flutter/test/cupertino/list_tile_test.dart
+482
-0
No files found.
examples/api/lib/cupertino/list_section/list_section_base.0.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for base CupertinoListSection and CupertinoListTile.
import
'package:flutter/cupertino.dart'
;
void
main
(
)
=>
runApp
(
const
CupertinoListSectionBaseApp
());
class
CupertinoListSectionBaseApp
extends
StatelessWidget
{
const
CupertinoListSectionBaseApp
({
super
.
key
});
static
const
String
_title
=
'Flutter Code Sample'
;
@override
Widget
build
(
BuildContext
context
)
{
return
const
CupertinoApp
(
title:
_title
,
home:
MyStatelessWidget
(),
);
}
}
class
MyStatelessWidget
extends
StatelessWidget
{
const
MyStatelessWidget
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
CupertinoPageScaffold
(
child:
CupertinoListSection
(
header:
const
Text
(
'My Reminders'
),
children:
<
CupertinoListTile
>[
CupertinoListTile
(
title:
const
Text
(
'Open pull request'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
activeGreen
,
),
trailing:
const
CupertinoListTileChevron
(),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
_SecondPage
(
text:
'Open pull request'
);
},
),
),
),
CupertinoListTile
(
title:
const
Text
(
'Push to master'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
systemRed
,
),
additionalInfo:
const
Text
(
'Not available'
),
),
CupertinoListTile
(
title:
const
Text
(
'View last commit'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
activeOrange
,
),
additionalInfo:
const
Text
(
'12 days ago'
),
trailing:
const
CupertinoListTileChevron
(),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
_SecondPage
(
text:
'Last commit'
);
},
),
),
),
],
),
);
}
}
class
_SecondPage
extends
StatelessWidget
{
const
_SecondPage
({
required
this
.
text
});
final
String
text
;
@override
Widget
build
(
BuildContext
context
)
{
return
CupertinoPageScaffold
(
child:
Center
(
child:
Text
(
text
),
),
);
}
}
examples/api/lib/cupertino/list_section/list_section_inset.0.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for inset CupertinoListSection and CupertinoListTile.
import
'package:flutter/cupertino.dart'
;
void
main
(
)
=>
runApp
(
const
CupertinoListSectionInsetApp
());
class
CupertinoListSectionInsetApp
extends
StatelessWidget
{
const
CupertinoListSectionInsetApp
({
super
.
key
});
static
const
String
_title
=
'Flutter Code Sample'
;
@override
Widget
build
(
BuildContext
context
)
{
return
const
CupertinoApp
(
title:
_title
,
home:
MyStatelessWidget
(),
);
}
}
class
MyStatelessWidget
extends
StatelessWidget
{
const
MyStatelessWidget
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
CupertinoPageScaffold
(
child:
CupertinoListSection
.
insetGrouped
(
header:
const
Text
(
'My Reminders'
),
children:
<
CupertinoListTile
>[
CupertinoListTile
.
notched
(
title:
const
Text
(
'Open pull request'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
activeGreen
,
),
trailing:
const
CupertinoListTileChevron
(),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
_SecondPage
(
text:
'Open pull request'
);
},
),
),
),
CupertinoListTile
.
notched
(
title:
const
Text
(
'Push to master'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
systemRed
,
),
additionalInfo:
const
Text
(
'Not available'
),
),
CupertinoListTile
.
notched
(
title:
const
Text
(
'View last commit'
),
leading:
Container
(
width:
double
.
infinity
,
height:
double
.
infinity
,
color:
CupertinoColors
.
activeOrange
,
),
additionalInfo:
const
Text
(
'12 days ago'
),
trailing:
const
CupertinoListTileChevron
(),
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
_SecondPage
(
text:
'Last commit'
);
},
),
),
),
],
),
);
}
}
class
_SecondPage
extends
StatelessWidget
{
const
_SecondPage
({
required
this
.
text
});
final
String
text
;
@override
Widget
build
(
BuildContext
context
)
{
return
CupertinoPageScaffold
(
child:
Center
(
child:
Text
(
text
),
),
);
}
}
examples/api/test/cupertino/list_section/list_section_base.0_test.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter_api_samples/cupertino/list_section/list_section_base.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Has exactly 1 CupertinoListSection base widget'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
CupertinoListSectionBaseApp
(),
);
final
Finder
listSectionFinder
=
find
.
byType
(
CupertinoListSection
);
expect
(
listSectionFinder
,
findsOneWidget
);
final
CupertinoListSection
listSectionWidget
=
tester
.
widget
<
CupertinoListSection
>(
listSectionFinder
);
expect
(
listSectionWidget
.
type
,
equals
(
CupertinoListSectionType
.
base
));
});
testWidgets
(
'CupertinoListSection has 3 CupertinoListTile children'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
CupertinoListSectionBaseApp
(),
);
expect
(
find
.
byType
(
CupertinoListTile
),
findsNWidgets
(
3
));
});
}
examples/api/test/cupertino/list_section/list_section_inset.0_test.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter_api_samples/cupertino/list_section/list_section_inset.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Has exactly 1 CupertinoListSection inset grouped widget'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
CupertinoListSectionInsetApp
(),
);
final
Finder
listSectionFinder
=
find
.
byType
(
CupertinoListSection
);
expect
(
listSectionFinder
,
findsOneWidget
);
final
CupertinoListSection
listSectionWidget
=
tester
.
widget
<
CupertinoListSection
>(
listSectionFinder
);
expect
(
listSectionWidget
.
type
,
equals
(
CupertinoListSectionType
.
insetGrouped
));
});
testWidgets
(
'CupertinoListSection has 3 CupertinoListTile children'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
CupertinoListSectionInsetApp
(),
);
expect
(
find
.
byType
(
CupertinoListTile
),
findsNWidgets
(
3
));
});
}
packages/flutter/lib/cupertino.dart
View file @
0c40945a
...
@@ -38,6 +38,8 @@ export 'src/cupertino/form_section.dart';
...
@@ -38,6 +38,8 @@ export 'src/cupertino/form_section.dart';
export
'src/cupertino/icon_theme_data.dart'
;
export
'src/cupertino/icon_theme_data.dart'
;
export
'src/cupertino/icons.dart'
;
export
'src/cupertino/icons.dart'
;
export
'src/cupertino/interface_level.dart'
;
export
'src/cupertino/interface_level.dart'
;
export
'src/cupertino/list_section.dart'
;
export
'src/cupertino/list_tile.dart'
;
export
'src/cupertino/localizations.dart'
;
export
'src/cupertino/localizations.dart'
;
export
'src/cupertino/nav_bar.dart'
;
export
'src/cupertino/nav_bar.dart'
;
export
'src/cupertino/page_scaffold.dart'
;
export
'src/cupertino/page_scaffold.dart'
;
...
...
packages/flutter/lib/src/cupertino/form_section.dart
View file @
0c40945a
...
@@ -5,28 +5,17 @@
...
@@ -5,28 +5,17 @@
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'list_section.dart'
;
// Standard header margin, determined from SwiftUI's Forms in iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultHeaderMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
16.0
,
20.0
,
10.0
);
// Standard footer margin, determined from SwiftUI's Forms in iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultFooterMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
// Used for iOS "Inset Grouped" margin, determined from SwiftUI's Forms in
// Used for iOS "Inset Grouped" margin, determined from SwiftUI's Forms in
// iOS 14.2 SDK.
// iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultInsetGroupedRowsMargin
=
const
EdgeInsetsDirectional
_kFormDefaultInsetGroupedRowsMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
// Used for iOS "Inset Grouped" border radius, estimated from SwiftUI's Forms in
// Standard header margin, determined from SwiftUI's Forms in iOS 14.2 SDK.
// iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kFormDefaultHeaderMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
16.0
,
20.0
,
10.0
);
// TODO(edrisian): This should be a rounded rectangle once that shape is added.
const
BorderRadius
_kDefaultInsetGroupedBorderRadius
=
BorderRadius
.
all
(
Radius
.
circular
(
10.0
));
//
Used to differentiate the edge-to-edge section with the centered section
.
//
Standard footer margin, determined from SwiftUI's Forms in iOS 14.2 SDK
.
enum
_CupertinoFormSectionType
{
base
,
insetGrouped
}
const
EdgeInsetsDirectional
_kFormDefaultFooterMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
/// An iOS-style form section.
/// An iOS-style form section.
///
///
...
@@ -65,6 +54,12 @@ enum _CupertinoFormSectionType { base, insetGrouped }
...
@@ -65,6 +54,12 @@ enum _CupertinoFormSectionType { base, insetGrouped }
/// If null, defaults to [CupertinoColors.systemGroupedBackground].
/// If null, defaults to [CupertinoColors.systemGroupedBackground].
///
///
/// {@macro flutter.material.Material.clipBehavior}
/// {@macro flutter.material.Material.clipBehavior}
///
/// See also:
///
/// * [CupertinoFormRow], an iOS-style list tile, a typical child of
/// [CupertinoFormSection].
/// * [CupertinoListSection], an iOS-style list section.
class
CupertinoFormSection
extends
StatelessWidget
{
class
CupertinoFormSection
extends
StatelessWidget
{
/// Creates a section that mimics standard iOS forms.
/// Creates a section that mimics standard iOS forms.
///
///
...
@@ -107,7 +102,7 @@ class CupertinoFormSection extends StatelessWidget {
...
@@ -107,7 +102,7 @@ class CupertinoFormSection extends StatelessWidget {
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
decoration
,
this
.
decoration
,
this
.
clipBehavior
=
Clip
.
none
,
this
.
clipBehavior
=
Clip
.
none
,
})
:
_type
=
_CupertinoForm
SectionType
.
base
,
})
:
_type
=
CupertinoList
SectionType
.
base
,
assert
(
children
.
length
>
0
);
assert
(
children
.
length
>
0
);
/// Creates a section that mimics standard "Inset Grouped" iOS forms.
/// Creates a section that mimics standard "Inset Grouped" iOS forms.
...
@@ -149,14 +144,14 @@ class CupertinoFormSection extends StatelessWidget {
...
@@ -149,14 +144,14 @@ class CupertinoFormSection extends StatelessWidget {
required
this
.
children
,
required
this
.
children
,
this
.
header
,
this
.
header
,
this
.
footer
,
this
.
footer
,
this
.
margin
=
_kDefaultInsetGroupedRowsMargin
,
this
.
margin
=
_k
Form
DefaultInsetGroupedRowsMargin
,
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
decoration
,
this
.
decoration
,
this
.
clipBehavior
=
Clip
.
none
,
this
.
clipBehavior
=
Clip
.
none
,
})
:
_type
=
_CupertinoForm
SectionType
.
insetGrouped
,
})
:
_type
=
CupertinoList
SectionType
.
insetGrouped
,
assert
(
children
.
length
>
0
);
assert
(
children
.
length
>
0
);
final
_CupertinoForm
SectionType
_type
;
final
CupertinoList
SectionType
_type
;
/// Sets the form section header. The section header lies above the
/// Sets the form section header. The section header lies above the
/// [children] rows.
/// [children] rows.
...
@@ -203,116 +198,48 @@ class CupertinoFormSection extends StatelessWidget {
...
@@ -203,116 +198,48 @@ class CupertinoFormSection extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
Color
dividerColor
=
CupertinoColors
.
separator
.
resolveFrom
(
context
);
final
Widget
?
headerWidget
=
header
==
null
final
double
dividerHeight
=
1.0
/
MediaQuery
.
of
(
context
).
devicePixelRatio
;
?
null
:
DefaultTextStyle
(
// Long divider is used for wrapping the top and bottom of rows.
style:
TextStyle
(
// Only used in _CupertinoFormSectionType.base mode
fontSize:
13.0
,
final
Widget
longDivider
=
Container
(
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
color:
dividerColor
,
height:
dividerHeight
,
);
// Short divider is used between rows.
// The value of the starting inset (15.0) is determined using SwiftUI's Form
// separators in the iOS 14.2 SDK.
final
Widget
shortDivider
=
Container
(
margin:
const
EdgeInsetsDirectional
.
only
(
start:
15.0
),
color:
dividerColor
,
height:
dividerHeight
,
);
// We construct childrenWithDividers as follows:
// Insert a short divider between all rows.
// If it is a `_CupertinoFormSectionType.base` type, add a long divider
// to the top and bottom of the rows.
assert
(
children
.
isNotEmpty
);
final
List
<
Widget
>
childrenWithDividers
=
<
Widget
>[];
if
(
_type
==
_CupertinoFormSectionType
.
base
)
{
childrenWithDividers
.
add
(
longDivider
);
}
children
.
sublist
(
0
,
children
.
length
-
1
).
forEach
((
Widget
widget
)
{
childrenWithDividers
.
add
(
widget
);
childrenWithDividers
.
add
(
shortDivider
);
});
childrenWithDividers
.
add
(
children
.
last
);
if
(
_type
==
_CupertinoFormSectionType
.
base
)
{
childrenWithDividers
.
add
(
longDivider
);
}
final
BorderRadius
childrenGroupBorderRadius
;
switch
(
_type
)
{
case
_CupertinoFormSectionType
.
insetGrouped
:
childrenGroupBorderRadius
=
_kDefaultInsetGroupedBorderRadius
;
break
;
case
_CupertinoFormSectionType
.
base
:
childrenGroupBorderRadius
=
BorderRadius
.
zero
;
break
;
}
// Refactored the decorate children group in one place to avoid repeating it
// twice down bellow in the returned widget.
final
DecoratedBox
decoratedChildrenGroup
=
DecoratedBox
(
decoration:
decoration
??
BoxDecoration
(
color:
CupertinoDynamicColor
.
resolve
(
decoration
?.
color
??
CupertinoColors
.
secondarySystemGroupedBackground
,
context
,
),
borderRadius:
childrenGroupBorderRadius
,
),
child:
Column
(
children:
childrenWithDividers
,
),
);
return
DecoratedBox
(
decoration:
BoxDecoration
(
color:
CupertinoDynamicColor
.
resolve
(
backgroundColor
,
context
),
),
child:
Column
(
children:
<
Widget
>[
if
(
header
!=
null
)
Align
(
alignment:
AlignmentDirectional
.
centerStart
,
child:
DefaultTextStyle
(
style:
TextStyle
(
fontSize:
13.0
,
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
),
child:
Padding
(
padding:
_kDefaultHeaderMargin
,
child:
header
,
),
),
),
Padding
(
padding:
margin
,
child:
ClipRRect
(
borderRadius:
childrenGroupBorderRadius
,
clipBehavior:
clipBehavior
,
child:
decoratedChildrenGroup
,
),
),
),
child:
Padding
(
if
(
footer
!=
null
)
padding:
_kFormDefaultHeaderMargin
,
Align
(
child:
header
,
alignment:
AlignmentDirectional
.
centerStart
,
));
child:
DefaultTextStyle
(
style:
TextStyle
(
final
Widget
?
footerWidget
=
footer
==
null
fontSize:
13.0
,
?
null
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
:
DefaultTextStyle
(
),
style:
TextStyle
(
child:
Padding
(
fontSize:
13.0
,
padding:
_kDefaultFooterMargin
,
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
child:
footer
,
),
),
),
),
],
child:
Padding
(
),
padding:
_kFormDefaultFooterMargin
,
);
child:
footer
,
));
return
_type
==
CupertinoListSectionType
.
base
?
CupertinoListSection
(
header:
headerWidget
,
footer:
footerWidget
,
margin:
margin
,
backgroundColor:
backgroundColor
,
decoration:
decoration
,
clipBehavior:
clipBehavior
,
hasLeading:
false
,
children:
children
)
:
CupertinoListSection
.
insetGrouped
(
header:
headerWidget
,
footer:
footerWidget
,
margin:
margin
,
backgroundColor:
backgroundColor
,
decoration:
decoration
,
clipBehavior:
clipBehavior
,
hasLeading:
false
,
children:
children
);
}
}
}
}
packages/flutter/lib/src/cupertino/list_section.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'theme.dart'
;
// Margin on top of the list section. This was eyeballed from iOS 14.4 Simulator
// and should be always present on top of the edge-to-edge variant.
const
double
_kMarginTop
=
22.0
;
// Standard header margin, determined from SwiftUI's Forms in iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultHeaderMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
6.0
);
// Header margin for inset grouped variant, determined from iOS 14.4 Simulator.
const
EdgeInsetsDirectional
_kInsetGroupedDefaultHeaderMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
16.0
,
20.0
,
6.0
);
// Standard footer margin, determined from SwiftUI's Forms in iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultFooterMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
0.0
);
// Footer margin for inset grouped variant, determined from iOS 14.4 Simulator.
const
EdgeInsetsDirectional
_kInsetGroupedDefaultFooterMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
// Margin around children in edge-to-edge variant, determined from iOS 14.4
// Simulator.
const
EdgeInsets
_kDefaultRowsMargin
=
EdgeInsets
.
only
(
bottom:
8.0
);
// Used for iOS "Inset Grouped" margin, determined from SwiftUI's Forms in
// iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultInsetGroupedRowsMargin
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
20.0
,
20.0
,
10.0
);
// Used for iOS "Inset Grouped" margin, determined from SwiftUI's Forms in
// iOS 14.2 SDK.
const
EdgeInsetsDirectional
_kDefaultInsetGroupedRowsMarginWithHeader
=
EdgeInsetsDirectional
.
fromSTEB
(
20.0
,
0.0
,
20.0
,
10.0
);
// Used for iOS "Inset Grouped" border radius, estimated from SwiftUI's Forms in
// iOS 14.2 SDK.
// TODO(edrisian): This should be a rounded rectangle once that shape is added.
const
BorderRadius
_kDefaultInsetGroupedBorderRadius
=
BorderRadius
.
all
(
Radius
.
circular
(
10.0
));
// The margin of divider used in base list section. Estimated from iOS 14.4 SDK
// Settings app.
const
double
_kBaseDividerMargin
=
20.0
;
// Additional margin of divider used in base list section with list tiles with
// leading widgets. Estimated from iOS 14.4 SDK Settings app.
const
double
_kBaseAdditionalDividerMargin
=
44.0
;
// The margin of divider used in inset grouped version of list section.
// Estimated from iOS 14.4 SDK Reminders app.
const
double
_kInsetDividerMargin
=
14.0
;
// Additional margin of divider used in inset grouped version of list section.
// Estimated from iOS 14.4 SDK Reminders app.
const
double
_kInsetAdditionalDividerMargin
=
42.0
;
// Additional margin of divider used in inset grouped version of list section
// when there is no leading widgets. Estimated from iOS 14.4 SDK Notes app.
const
double
_kInsetAdditionalDividerMarginWithoutLeading
=
14.0
;
// Color of header and footer text in edge-to-edge variant.
const
Color
_kHeaderFooterColor
=
CupertinoDynamicColor
(
color:
Color
.
fromRGBO
(
108
,
108
,
108
,
1.0
),
darkColor:
Color
.
fromRGBO
(
142
,
142
,
146
,
1.0
),
highContrastColor:
Color
.
fromRGBO
(
74
,
74
,
77
,
1.0
),
darkHighContrastColor:
Color
.
fromRGBO
(
176
,
176
,
183
,
1.0
),
elevatedColor:
Color
.
fromRGBO
(
108
,
108
,
108
,
1.0
),
darkElevatedColor:
Color
.
fromRGBO
(
142
,
142
,
146
,
1.0
),
highContrastElevatedColor:
Color
.
fromRGBO
(
108
,
108
,
108
,
1.0
),
darkHighContrastElevatedColor:
Color
.
fromRGBO
(
142
,
142
,
146
,
1.0
),
);
/// Denotes what type of the list section a [CupertinoListSection] is.
///
/// This is for internal use only.
enum
CupertinoListSectionType
{
/// A basic form of [CupertinoListSection].
base
,
/// An inset-grouped style of [CupertinoListSection].
insetGrouped
,
}
/// An iOS-style list section.
///
/// The [CupertinoListSection] is a container for children widgets. These are
/// most often [CupertinoListTile]s.
///
/// The base constructor for [CupertinoListSection] constructs an
/// edge-to-edge style section which includes an iOS-style header, the dividers
/// between rows, and borders on top and bottom of the rows. An example of such
/// list section are sections in iOS Settings app.
///
/// The [CupertinoListSection.insetGrouped] constructor creates a round-edged
/// and padded section that is seen in iOS Notes and Reminders apps. It creates
/// an iOS-style header, and the dividers between rows. Does not create borders
/// on top and bottom of the rows.
///
/// The section [header] lies above the [children] rows, with margins and style
/// that match the iOS style.
///
/// The section [footer] lies below the [children] rows and is used to provide
/// additional information for current list section.
///
/// The [children] is the list of widgets to be displayed in this list section.
/// Typically, the children are of type [CupertinoListTile], however these is
/// not enforced.
///
/// The [margin] is used to provide spacing around the content area of the
/// section encapsulating [children].
///
/// The [decoration] of [children] specifies how they should be decorated. If it
/// is not provided in constructor, the background color of [children] defaults
/// to [CupertinoColors.secondarySystemGroupedBackground] and border radius of
/// children group defaults to 10.0 circular radius when constructing with
/// [CupertinoListSection.insetGrouped]. Defaults to zero radius for the
/// standard [CupertinoListSection] constructor.
///
/// The [dividerMargin] and [additionalDividerMargin] specify the starting
/// margin of the divider between list tiles. The [dividerMargin] is always
/// present, but [additionalDividerMargin] is only added to the [dividerMargin]
/// if `hasLeading` is set to true in the constructor, which is the default
/// value.
///
/// The [backgroundColor] of the section defaults to
/// [CupertinoColors.systemGroupedBackground].
///
/// {@macro flutter.material.Material.clipBehavior}
///
/// {@tool dartpad}
/// Creates a base [CupertinoListSection] containing [CupertinoListTile]s with
/// `leading`, `title`, `additionalInfo` and `trailing` widgets.
///
/// ** See code in examples/api/lib/cupertino/list_section/list_section_base.0.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// Creates an "Inset Grouped" [CupertinoListSection] containing
/// notched [CupertinoListTile]s with `leading`, `title`, `additionalInfo` and
/// `trailing` widgets.
///
/// ** See code in examples/api/lib/cupertino/list_section/list_section_inset.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [CupertinoListTile], an iOS-style list tile, a typical child of
/// [CupertinoListSection].
/// * [CupertinoFormSection], an iOS-style form section.
class
CupertinoListSection
extends
StatelessWidget
{
/// Creates a section that mimics standard iOS forms.
///
/// The base constructor for [CupertinoListSection] constructs an
/// edge-to-edge style section which includes an iOS-style header, the dividers
/// between rows, and borders on top and bottom of the rows. An example of such
/// list section are sections in iOS Settings app.
///
/// The [header] parameter sets the form section header. The section header
/// lies above the [children] rows, with margins that match the iOS style.
///
/// The [footer] parameter sets the form section footer. The section footer
/// lies below the [children] rows.
///
/// The [children] parameter is required and sets the list of rows shown in
/// the section. The [children] parameter takes a list, as opposed to a more
/// efficient builder function that lazy builds, because forms are intended to
/// be short in row count. It is recommended that only [CupertinoFormRow] and
/// [CupertinoTextFormFieldRow] widgets be included in the [children] list in
/// order to retain the iOS look.
///
/// The [margin] parameter sets the spacing around the content area of the
/// section encapsulating [children], and defaults to zero padding.
///
/// The [decoration] parameter sets the decoration around [children].
/// If null, defaults to [CupertinoColors.secondarySystemGroupedBackground].
/// If null, defaults to 10.0 circular radius when constructing with
/// [CupertinoListSection.insetGrouped]. Defaults to zero radius for the
/// standard [CupertinoListSection] constructor.
///
/// The [backgroundColor] parameter sets the background color behind the
/// section. If null, defaults to [CupertinoColors.systemGroupedBackground].
///
/// The [dividerMargin] parameter sets the starting offset of the divider
/// between rows.
///
/// The [additionalDividerMargin] parameter adds additional margin to existing
/// [dividerMargin] when [hasLeading] is set to true. By default, it offsets
/// for the width of leading and space between leading and title of
/// [CupertinoListTile], but it can be overwritten for custom look.
///
/// The [hasLeading] parameter specifies whether children [CupertinoListTile]
/// widgets contain leading or not. Used for calculating correct starting
/// margin for the divider between rows.
///
/// The [topMargin] is used to specify the margin above the list section. It
/// matches the iOS look by default.
///
/// {@macro flutter.material.Material.clipBehavior}
const
CupertinoListSection
({
super
.
key
,
this
.
children
,
this
.
header
,
this
.
footer
,
this
.
margin
=
_kDefaultRowsMargin
,
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
decoration
,
this
.
clipBehavior
=
Clip
.
none
,
this
.
dividerMargin
=
_kBaseDividerMargin
,
double
?
additionalDividerMargin
,
this
.
topMargin
=
_kMarginTop
,
bool
hasLeading
=
true
,
})
:
assert
((
children
!=
null
&&
children
.
length
>
0
)
||
header
!=
null
),
type
=
CupertinoListSectionType
.
base
,
additionalDividerMargin
=
additionalDividerMargin
??
(
hasLeading
?
_kBaseAdditionalDividerMargin
:
0.0
);
/// Creates a section that mimicks standard "Inset Grouped" iOS list section.
///
/// The [CupertinoListSection.insetGrouped] constructor creates a round-edged
/// and padded section that is seen in iOS Notes and Reminders apps. It creates
/// an iOS-style header, and the dividers between rows. Does not create borders
/// on top and bottom of the rows.
///
/// The [header] parameter sets the form section header. The section header
/// lies above the [children] rows, with margins that match the iOS style.
///
/// The [footer] parameter sets the form section footer. The section footer
/// lies below the [children] rows.
///
/// The [children] parameter is required and sets the list of rows shown in
/// the section. The [children] parameter takes a list, as opposed to a more
/// efficient builder function that lazy builds, because forms are intended to
/// be short in row count. It is recommended that only [CupertinoListTile]
/// widget be included in the [children] list in order to retain the iOS look.
///
/// The [margin] parameter sets the spacing around the content area of the
/// section encapsulating [children], and defaults to the standard
/// notched-style iOS form padding.
///
/// The [decoration] parameter sets the decoration around [children].
/// If null, defaults to [CupertinoColors.secondarySystemGroupedBackground].
/// If null, defaults to 10.0 circular radius when constructing with
/// [CupertinoListSection.insetGrouped]. Defaults to zero radius for the
/// standard [CupertinoListSection] constructor.
///
/// The [backgroundColor] parameter sets the background color behind the
/// section. If null, defaults to [CupertinoColors.systemGroupedBackground].
///
/// The [dividerMargin] parameter sets the starting offset of the divider
/// between rows.
///
/// The [additionalDividerMargin] parameter adds additional margin to existing
/// [dividerMargin] when [hasLeading] is set to true. By default, it offsets
/// for the width of leading and space between leading and title of
/// [CupertinoListTile], but it can be overwritten for custom look.
///
/// The [hasLeading] parameter specifies whether children [CupertinoListTile]
/// widgets contain leading or not. Used for calculating correct starting
/// margin for the divider between rows.
///
/// {@macro flutter.material.Material.clipBehavior}
const
CupertinoListSection
.
insetGrouped
({
super
.
key
,
this
.
children
,
this
.
header
,
this
.
footer
,
EdgeInsetsGeometry
?
margin
,
this
.
backgroundColor
=
CupertinoColors
.
systemGroupedBackground
,
this
.
decoration
,
this
.
clipBehavior
=
Clip
.
hardEdge
,
this
.
dividerMargin
=
_kInsetDividerMargin
,
double
?
additionalDividerMargin
,
this
.
topMargin
,
bool
hasLeading
=
true
,
})
:
assert
((
children
!=
null
&&
children
.
length
>
0
)
||
header
!=
null
),
type
=
CupertinoListSectionType
.
insetGrouped
,
additionalDividerMargin
=
additionalDividerMargin
??
(
hasLeading
?
_kInsetAdditionalDividerMargin
:
_kInsetAdditionalDividerMarginWithoutLeading
),
margin
=
margin
??
(
header
==
null
?
_kDefaultInsetGroupedRowsMargin
:
_kDefaultInsetGroupedRowsMarginWithHeader
);
/// The type of list section, either base or inset grouped.
///
/// This member is public for testing purposes only and cannot be set
/// manually. Instead, use a corresponding constructors.
@visibleForTesting
final
CupertinoListSectionType
type
;
/// Sets the form section header. The section header lies above the [children]
/// rows. Usually a [Text] widget.
final
Widget
?
header
;
/// Sets the form section footer. The section footer lies below the [children]
/// rows. Usually a [Text] widget.
final
Widget
?
footer
;
/// Margin around the content area of the section encapsulating [children].
///
/// Defaults to zero padding if constructed with standard
/// [CupertinoListSection] constructor. Defaults to the standard notched-style
/// iOS margin when constructing with [CupertinoListSection.insetGrouped].
final
EdgeInsetsGeometry
margin
;
/// The list of rows in the section. Usually a list of [CupertinoListTile]s.
///
/// This takes a list, as opposed to a more efficient builder function that
/// lazy builds, because such lists are intended to be short in row count.
/// It is recommended that only [CupertinoListTile] widget be included in the
/// [children] list in order to retain the iOS look.
final
List
<
Widget
>?
children
;
/// Sets the decoration around [children].
///
/// If null, background color defaults to
/// [CupertinoColors.secondarySystemGroupedBackground].
///
/// If null, border radius defaults to 10.0 circular radius when constructing
/// with [CupertinoListSection.insetGrouped]. Defaults to zero radius for the
/// standard [CupertinoListSection] constructor.
final
BoxDecoration
?
decoration
;
/// Sets the background color behind the section.
///
/// Defaults to [CupertinoColors.systemGroupedBackground].
final
Color
backgroundColor
;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.hardEdge].
final
Clip
clipBehavior
;
/// The starting offset of a margin between two list tiles.
final
double
dividerMargin
;
/// Additional starting inset of the divider used between rows. This is used
/// when adding a leading icon to children and a divider should start at the
/// text inset instead of the icon.
final
double
additionalDividerMargin
;
/// Margin above the list section. Only used in edge-to-edge variant and it
/// matches iOS style by default.
final
double
?
topMargin
;
@override
Widget
build
(
BuildContext
context
)
{
final
Color
dividerColor
=
CupertinoColors
.
separator
.
resolveFrom
(
context
);
final
double
dividerHeight
=
1.0
/
MediaQuery
.
of
(
context
).
devicePixelRatio
;
// Long divider is used for wrapping the top and bottom of rows.
// Only used in CupertinoListSectionType.base mode.
final
Widget
longDivider
=
Container
(
color:
dividerColor
,
height:
dividerHeight
,
);
// Short divider is used between rows.
final
Widget
shortDivider
=
Container
(
margin:
EdgeInsetsDirectional
.
only
(
start:
dividerMargin
+
additionalDividerMargin
),
color:
dividerColor
,
height:
dividerHeight
,
);
Widget
?
headerWidget
;
if
(
header
!=
null
)
{
headerWidget
=
DefaultTextStyle
(
style:
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
type
==
CupertinoListSectionType
.
base
?
TextStyle
(
fontSize:
13.0
,
color:
CupertinoDynamicColor
.
resolve
(
_kHeaderFooterColor
,
context
))
:
const
TextStyle
(
fontSize:
20.0
,
fontWeight:
FontWeight
.
bold
),
),
child:
header
!,
);
}
Widget
?
footerWidget
;
if
(
footer
!=
null
)
{
footerWidget
=
DefaultTextStyle
(
style:
type
==
CupertinoListSectionType
.
base
?
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
TextStyle
(
fontSize:
13.0
,
color:
CupertinoDynamicColor
.
resolve
(
_kHeaderFooterColor
,
context
),
))
:
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
,
child:
footer
!,
);
}
BorderRadius
?
childrenGroupBorderRadius
;
DecoratedBox
?
decoratedChildrenGroup
;
if
(
children
!=
null
&&
children
!.
isNotEmpty
)
{
// We construct childrenWithDividers as follows:
// Insert a short divider between all rows.
// If it is a `CupertinoListSectionType.base` type, add a long divider
// to the top and bottom of the rows.
final
List
<
Widget
>
childrenWithDividers
=
<
Widget
>[];
if
(
type
==
CupertinoListSectionType
.
base
)
{
childrenWithDividers
.
add
(
longDivider
);
}
children
!.
sublist
(
0
,
children
!.
length
-
1
).
forEach
((
Widget
widget
)
{
childrenWithDividers
.
add
(
widget
);
childrenWithDividers
.
add
(
shortDivider
);
});
childrenWithDividers
.
add
(
children
!.
last
);
if
(
type
==
CupertinoListSectionType
.
base
)
{
childrenWithDividers
.
add
(
longDivider
);
}
switch
(
type
)
{
case
CupertinoListSectionType
.
insetGrouped
:
childrenGroupBorderRadius
=
_kDefaultInsetGroupedBorderRadius
;
break
;
case
CupertinoListSectionType
.
base
:
childrenGroupBorderRadius
=
BorderRadius
.
zero
;
break
;
}
// Refactored the decorate children group in one place to avoid repeating it
// twice down bellow in the returned widget.
decoratedChildrenGroup
=
DecoratedBox
(
decoration:
decoration
??
BoxDecoration
(
color:
CupertinoDynamicColor
.
resolve
(
decoration
?.
color
??
CupertinoColors
.
secondarySystemGroupedBackground
,
context
),
borderRadius:
childrenGroupBorderRadius
,
),
child:
Column
(
children:
childrenWithDividers
),
);
}
return
DecoratedBox
(
decoration:
BoxDecoration
(
color:
CupertinoDynamicColor
.
resolve
(
backgroundColor
,
context
)),
child:
Column
(
children:
<
Widget
>[
if
(
type
==
CupertinoListSectionType
.
base
)
SizedBox
(
height:
topMargin
),
if
(
headerWidget
!=
null
)
Align
(
alignment:
AlignmentDirectional
.
centerStart
,
child:
Padding
(
padding:
type
==
CupertinoListSectionType
.
base
?
_kDefaultHeaderMargin
:
_kInsetGroupedDefaultHeaderMargin
,
child:
headerWidget
,
),
),
if
(
children
!=
null
&&
children
!.
isNotEmpty
)
Padding
(
padding:
margin
,
child:
clipBehavior
==
Clip
.
none
?
decoratedChildrenGroup
:
ClipRRect
(
borderRadius:
childrenGroupBorderRadius
,
clipBehavior:
clipBehavior
,
child:
decoratedChildrenGroup
,
),
),
if
(
footerWidget
!=
null
)
Align
(
alignment:
AlignmentDirectional
.
centerStart
,
child:
Padding
(
padding:
type
==
CupertinoListSectionType
.
base
?
_kDefaultFooterMargin
:
_kInsetGroupedDefaultFooterMargin
,
child:
footerWidget
,
),
),
],
),
);
}
}
packages/flutter/lib/src/cupertino/list_tile.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'icons.dart'
;
import
'theme.dart'
;
// These constants were eyeballed from iOS 14.4 Settings app for base, Notes for
// notched without leading, and Reminders app for notched with leading.
const
double
_kLeadingSize
=
28.0
;
const
double
_kNotchedLeadingSize
=
30.0
;
const
double
_kMinHeight
=
_kLeadingSize
+
2
*
8.0
;
const
double
_kMinHeightWithSubtitle
=
_kLeadingSize
+
2
*
10.0
;
const
double
_kNotchedMinHeight
=
_kNotchedLeadingSize
+
2
*
12.0
;
const
double
_kNotchedMinHeightWithoutLeading
=
_kNotchedLeadingSize
+
2
*
10.0
;
const
EdgeInsetsDirectional
_kPadding
=
EdgeInsetsDirectional
.
only
(
start:
20.0
,
end:
14.0
);
const
EdgeInsetsDirectional
_kPaddingWithSubtitle
=
EdgeInsetsDirectional
.
only
(
start:
20.0
,
end:
14.0
);
const
EdgeInsets
_kNotchedPadding
=
EdgeInsets
.
symmetric
(
horizontal:
14.0
);
const
EdgeInsetsDirectional
_kNotchedPaddingWithoutLeading
=
EdgeInsetsDirectional
.
fromSTEB
(
28.0
,
10.0
,
14.0
,
10.0
);
const
double
_kLeadingToTitle
=
16.0
;
const
double
_kNotchedLeadingToTitle
=
12.0
;
const
double
_kNotchedTitleToSubtitle
=
3.0
;
const
double
_kAdditionalInfoToTrailing
=
6.0
;
const
double
_kNotchedTitleWithSubtitleFontSize
=
16.0
;
const
double
_kSubtitleFontSize
=
12.0
;
const
double
_kNotchedSubtitleFontSize
=
14.0
;
enum
_CupertinoListTileType
{
base
,
notched
}
/// An iOS-style list tile.
///
/// The [CupertinoListTile] is a Cupertino equivalent of Material [ListTile].
/// It comes in two forms, an old-fashioned edge-to-edge variant known from iOS
/// Settings app and in a new, "Inset Grouped" form, known from either iOS Notes
/// or Reminders app. The first is constructed using default constructor, and
/// the latter using named constructor [CupertinoListTile.notched].
///
/// The [title], [subtitle], and [additionalInfo] are usually [Text] widgets.
/// They are all limited to one line so it is a responsibility of the caller to
/// take care of text wrapping.
///
/// The size of [leading] is by default constrained to match the iOS size,
/// depending of the type of list tile. This can however be overriden by
/// providing [leadingSize]. The [trailing] widget is not constrained and is
/// therefore a responsibility of the caller to ensure reasonable size of the
/// [trailing] widget.
///
/// The background color of the tile can be set with [backgroundColor] for the
/// state before tile was tapped and with [backgroundColorActivated] for the
/// state after the tile was tapped. By default, both values are set to match
/// the default iOS appearance.
///
/// The [padding] and [leadingToTitle] are by default set to match iOS but can
/// be overwritten if necessary.
///
/// The [onTap] callback provides an option to react to taps anywhere inside the
/// list tile. This can be used to navigate routes and according to iOS
/// behaviour it should not be used for example to toggle the [CupertinoSwitch]
/// in the trailing widget.
///
/// See also:
///
/// * [CupertinoListSection], an iOS-style list that is a typical container for
/// [CupertinoListTile].
/// * [ListTile], a Material Design list tile.
class
CupertinoListTile
extends
StatefulWidget
{
/// Creates an edge-to-edge iOS-style list tile like the tiles in iOS Settings
/// app.
///
/// The [title] parameter is required. It is used to convey the most important
/// information of list tile. It is typically a [Text].
///
/// The [subtitle] parameter is used to display additional information. It is
/// placed below the [title].
///
/// The [additionalInfo] parameter is used to display additional information.
/// It is placed at the end of the tile, before the [trailing] if supplied.
///
/// The [leading] parameter is typically an [Icon] or an [Image] and it comes
/// at the start of the tile. If omitted in all list tiles, a `hasLeading` of
/// enclosing [CupertinoListSection] should be set to `false` to ensure
/// correct margin of divider between tiles.
///
/// The [trailing] parameter is typically a [CupertinoListTileChevron], an
/// [Icon], or a [CupertinoButton]. It is placed at the very end of the tile.
///
/// The [onTap] parameter is used to provide an action that is called when the
/// tile is tapped. It is mainly used for navigating to a new route. It should
/// not be used to toggle a trailing [CupertinoSwitch] and similar usecases
/// because when tile is tapped, it switches the background color and remains
/// changed. This is according to iOS behaviour.
///
/// The [backgroundColor] provides a custom background color for the tile in
/// a state before tapped. By default, it matches the theme's background color
/// which is by default a [CupertinoColors.systemBackground].
///
/// The [backgroundColorActivated] provides a custom background color for the
/// tile after it was tapped. By default, it matches the theme's background
/// color which is by default a [CupertinoColors.systemGrey4].
///
/// The [padding] parameter sets the padding of the content inside the tile.
/// It defaults to a value that matches the iOS look, depending on a type of
/// [CupertinoListTile]. For native look, it should not be provided.
///
/// The [leadingSize] constrains the width and height of the leading widget.
/// By default, it is set to a value that matches the iOS look, depending on a
/// type of [CupertinoListTile]. For native look, it should not be provided.
///
/// The [leadingToTitle] specifies the horizontal space between [leading] and
/// [title] widgets. By default, it is set to a value that matched the iOS
/// look, depending on a type of [CupertinoListTile]. For native look, it
/// should not be provided.
const
CupertinoListTile
({
super
.
key
,
required
this
.
title
,
this
.
subtitle
,
this
.
additionalInfo
,
this
.
leading
,
this
.
trailing
,
this
.
onTap
,
this
.
backgroundColor
,
this
.
backgroundColorActivated
,
this
.
padding
,
this
.
leadingSize
=
_kLeadingSize
,
this
.
leadingToTitle
=
_kLeadingToTitle
,
})
:
_type
=
_CupertinoListTileType
.
base
;
/// Creates a notched iOS-style list tile like the tiles in iOS Notes app or
/// Reminders app.
///
/// The [title] parameter is required. It is used to convey the most important
/// information of list tile. It is typically a [Text].
///
/// The [subtitle] parameter is used to display additional information. It is
/// placed below the [title].
///
/// The [additionalInfo] parameter is used to display additional information.
/// It is placed at the end of the tile, before the [trailing] if supplied.
///
/// The [leading] parameter is typically an [Icon] or an [Image] and it comes
/// at the start of the tile. If omitted in all list tiles, a `hasLeading` of
/// enclosing [CupertinoListSection] should be set to `false` to ensure
/// correct margin of divider between tiles. For Notes-like tile appearance,
/// the [leading] can be left `null`.
///
/// The [trailing] parameter is typically a [CupertinoListTileChevron], an
/// [Icon], or a [CupertinoButton]. It is placed at the very end of the tile.
/// For Notes-like tile appearance, the [trailing] can be left `null`.
///
/// The [onTap] parameter is used to provide an action that is called when the
/// tile is tapped. It is mainly used for navigating to a new route. It should
/// not be used to toggle a trailing [CupertinoSwitch] and similar usecases
/// because when tile is tapped, it switches the background color and remains
/// changed. This is according to iOS behaviour.
///
/// The [backgroundColor] provides a custom background color for the tile in
/// a state before tapped. By default, it matches the theme's background color
/// which is by default a [CupertinoColors.systemBackground].
///
/// The [backgroundColorActivated] provides a custom background color for the
/// tile after it was tapped. By default, it matches the theme's background
/// color which is by default a [CupertinoColors.systemGrey4].
///
/// The [padding] parameter sets the padding of the content inside the tile.
/// It defaults to a value that matches the iOS look, depending on a type of
/// [CupertinoListTile]. For native look, it should not be provided.
///
/// The [leadingSize] constrains the width and height of the leading widget.
/// By default, it is set to a value that matches the iOS look, depending on a
/// type of [CupertinoListTile]. For native look, it should not be provided.
///
/// The [leadingToTitle] specifies the horizontal space between [leading] and
/// [title] widgets. By default, it is set to a value that matched the iOS
/// look, depending on a type of [CupertinoListTile]. For native look, it
/// should not be provided.
const
CupertinoListTile
.
notched
({
super
.
key
,
required
this
.
title
,
this
.
subtitle
,
this
.
additionalInfo
,
this
.
leading
,
this
.
trailing
,
this
.
onTap
,
this
.
backgroundColor
,
this
.
backgroundColorActivated
,
this
.
padding
,
this
.
leadingSize
=
_kNotchedLeadingSize
,
this
.
leadingToTitle
=
_kNotchedLeadingToTitle
,
})
:
_type
=
_CupertinoListTileType
.
notched
;
final
_CupertinoListTileType
_type
;
/// A [title] is used to convey the central information. Usually a [Text].
final
Widget
title
;
/// A [subtitle] is used to display additional information. It is located
/// below [title]. Usually a [Text] widget.
final
Widget
?
subtitle
;
/// Similar to [subtitle], an [additionalInfo] is used to display additional
/// information. However, instead of being displayed below [title], it is
/// displayed on the right, before [trailing]. Usually a [Text] widget.
final
Widget
?
additionalInfo
;
/// A widget displayed at the start of the [CupertinoListTile]. This is
/// typically an `Icon` or an `Image`.
final
Widget
?
leading
;
/// A widget displayed at the end of the [CupertinoListTile]. This is usually
/// a right chevron icon (e.g. `CupertinoListTileChevron`), or an `Icon`.
final
Widget
?
trailing
;
/// The [onTap] function is called when a user taps on [CupertinoListTile]. If
/// left `null`, the [CupertinoListTile] will not react on taps. If this is a
/// `Future<void> Function()`, then the [CupertinoListTile] remains activated
/// until the returned future is awaited. This is according to iOS behaviour.
/// However, if this function is a `void Function()`, then the tile is active
/// only for the duration of invocation.
final
FutureOr
<
void
>
Function
()?
onTap
;
/// The [backgroundColor] of the tile in normal state. Once the tile is
/// tapped, the background color switches to [backgroundColorActivated]. It is
/// set to match the iOS look by default.
final
Color
?
backgroundColor
;
/// The [backgroundColorActivated] is the background color of the tile after
/// the tile was tapped. It is set to match the iOS look by default.
final
Color
?
backgroundColorActivated
;
/// Padding of the content inside [CupertinoListTile].
final
EdgeInsetsGeometry
?
padding
;
/// The [leadingSize] is used to constrain the width and height of [leading]
/// widget.
final
double
leadingSize
;
/// The horizontal space between [leading] widget and [title].
final
double
leadingToTitle
;
@override
State
<
CupertinoListTile
>
createState
()
=>
_CupertinoListTileState
();
}
class
_CupertinoListTileState
extends
State
<
CupertinoListTile
>
{
bool
_tapped
=
false
;
@override
Widget
build
(
BuildContext
context
)
{
final
TextStyle
titleTextStyle
=
widget
.
_type
==
_CupertinoListTileType
.
base
||
widget
.
subtitle
==
null
?
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
:
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
TextStyle
(
fontWeight:
FontWeight
.
w600
,
fontSize:
widget
.
leading
==
null
?
_kNotchedTitleWithSubtitleFontSize
:
null
,
),
);
final
TextStyle
subtitleTextStyle
=
widget
.
_type
==
_CupertinoListTileType
.
base
?
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
TextStyle
(
fontSize:
_kSubtitleFontSize
,
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
),
)
:
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
TextStyle
(
fontSize:
_kNotchedSubtitleFontSize
,
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
),
),
);
final
TextStyle
?
additionalInfoTextStyle
=
widget
.
additionalInfo
!=
null
?
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
merge
(
TextStyle
(
color:
CupertinoColors
.
secondaryLabel
.
resolveFrom
(
context
)))
:
null
;
final
Widget
title
=
DefaultTextStyle
(
style:
titleTextStyle
,
maxLines:
1
,
child:
widget
.
title
,
);
EdgeInsetsGeometry
?
padding
=
widget
.
padding
;
if
(
padding
==
null
)
{
switch
(
widget
.
_type
)
{
case
_CupertinoListTileType
.
base
:
padding
=
widget
.
subtitle
==
null
?
_kPadding
:
_kPaddingWithSubtitle
;
break
;
case
_CupertinoListTileType
.
notched
:
padding
=
widget
.
leading
==
null
?
_kNotchedPaddingWithoutLeading
:
_kNotchedPadding
;
break
;
}
}
Widget
?
subtitle
;
if
(
widget
.
subtitle
!=
null
)
{
subtitle
=
DefaultTextStyle
(
style:
subtitleTextStyle
,
maxLines:
1
,
child:
widget
.
subtitle
!,
);
}
Widget
?
additionalInfo
;
if
(
widget
.
additionalInfo
!=
null
)
{
additionalInfo
=
DefaultTextStyle
(
style:
additionalInfoTextStyle
!,
maxLines:
1
,
child:
widget
.
additionalInfo
!,
);
}
// The color for default state tile is set to either what user provided or
// null and it will resolve to the correct color provided by context. But if
// the tile was tapped, it is set to what user provided or if null to the
// default color that matched the iOS-style.
Color
?
backgroundColor
=
widget
.
backgroundColor
;
if
(
_tapped
)
{
backgroundColor
=
widget
.
backgroundColorActivated
??
CupertinoColors
.
systemGrey4
.
resolveFrom
(
context
);
}
double
minHeight
;
switch
(
widget
.
_type
)
{
case
_CupertinoListTileType
.
base
:
minHeight
=
subtitle
==
null
?
_kMinHeight
:
_kMinHeightWithSubtitle
;
break
;
case
_CupertinoListTileType
.
notched
:
minHeight
=
widget
.
leading
==
null
?
_kNotchedMinHeightWithoutLeading
:
_kNotchedMinHeight
;
break
;
}
final
Widget
child
=
Container
(
constraints:
BoxConstraints
(
minWidth:
double
.
infinity
,
minHeight:
minHeight
),
color:
backgroundColor
,
child:
Padding
(
padding:
padding
,
child:
Row
(
children:
<
Widget
>[
if
(
widget
.
leading
!=
null
)
...<
Widget
>[
SizedBox
(
width:
widget
.
leadingSize
,
height:
widget
.
leadingSize
,
child:
Center
(
child:
widget
.
leading
,
),
),
SizedBox
(
width:
widget
.
leadingToTitle
),
]
else
SizedBox
(
height:
widget
.
leadingSize
),
Column
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
title
,
if
(
subtitle
!=
null
)
...<
Widget
>[
const
SizedBox
(
height:
_kNotchedTitleToSubtitle
),
subtitle
,
],
],
),
const
Spacer
(),
if
(
additionalInfo
!=
null
)
...<
Widget
>[
additionalInfo
,
if
(
widget
.
trailing
!=
null
)
const
SizedBox
(
width:
_kAdditionalInfoToTrailing
),
],
if
(
widget
.
trailing
!=
null
)
widget
.
trailing
!
],
),
),
);
if
(
widget
.
onTap
==
null
)
{
return
child
;
}
return
GestureDetector
(
onTapDown:
(
_
)
=>
setState
(()
{
_tapped
=
true
;
}),
onTapCancel:
()
=>
setState
(()
{
_tapped
=
false
;
}),
onTap:
()
async
{
await
widget
.
onTap
!();
setState
(()
{
_tapped
=
false
;
});
},
behavior:
HitTestBehavior
.
opaque
,
child:
child
,
);
}
}
/// A typical iOS trailing widget used to denote that a `CupertinoListTile` is a
/// button with an action.
///
/// The [CupertinoListTileChevron] is meant as a convenience implementation of
/// trailing right chevron.
class
CupertinoListTileChevron
extends
StatelessWidget
{
/// Creates a typical widget used to denote that a `CupertinoListTile` is a
/// button with action.
const
CupertinoListTileChevron
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
Icon
(
CupertinoIcons
.
right_chevron
,
size:
CupertinoTheme
.
of
(
context
).
textTheme
.
textStyle
.
fontSize
,
color:
CupertinoColors
.
systemGrey2
.
resolveFrom
(
context
),
);
}
}
packages/flutter/test/cupertino/form_section_test.dart
View file @
0c40945a
...
@@ -153,7 +153,7 @@ void main() {
...
@@ -153,7 +153,7 @@ void main() {
expect
(
find
.
byType
(
ClipRRect
),
findsOneWidget
);
expect
(
find
.
byType
(
ClipRRect
),
findsOneWidget
);
});
});
testWidgets
(
'Not setting clipBehavior does not
clip children section
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Not setting clipBehavior does not
produce a RenderClipRRect object
'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
CupertinoApp
(
CupertinoApp
(
home:
Center
(
home:
Center
(
...
@@ -164,7 +164,7 @@ void main() {
...
@@ -164,7 +164,7 @@ void main() {
),
),
);
);
final
RenderClipRRect
renderClip
=
tester
.
allRenderObjects
.
whereType
<
RenderClipRRect
>().
first
;
final
Iterable
<
RenderClipRRect
>
renderClips
=
tester
.
allRenderObjects
.
whereType
<
RenderClipRRect
>()
;
expect
(
renderClip
.
clipBehavior
,
equals
(
Clip
.
none
)
);
expect
(
renderClip
s
,
isEmpty
);
});
});
}
}
packages/flutter/test/cupertino/list_section_test.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'shows header'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
header:
const
Text
(
'Header'
),
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
expect
(
find
.
text
(
'Header'
),
findsOneWidget
);
});
testWidgets
(
'shows footer'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
footer:
const
Text
(
'Footer'
),
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
expect
(
find
.
text
(
'Footer'
),
findsOneWidget
);
});
testWidgets
(
'shows long dividers in edge-to-edge section part 1'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
// Since the children list is reconstructed with dividers in it, the column
// retrieved should have 3 items for an input [children] param with 1 child.
final
Column
childrenColumn
=
tester
.
widget
(
find
.
byType
(
Column
).
at
(
1
));
expect
(
childrenColumn
.
children
.
length
,
3
);
});
testWidgets
(
'shows long dividers in edge-to-edge section part 2'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
// Since the children list is reconstructed with dividers in it, the column
// retrieved should have 5 items for an input [children] param with 2
// children. Two long dividers, two rows, and one short divider.
final
Column
childrenColumn
=
tester
.
widget
(
find
.
byType
(
Column
).
at
(
1
));
expect
(
childrenColumn
.
children
.
length
,
5
);
});
testWidgets
(
'does not show long dividers in insetGrouped section part 1'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
.
insetGrouped
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
// Since the children list is reconstructed without long dividers in it, the
// column retrieved should have 1 item for an input [children] param with 1
// child.
final
Column
childrenColumn
=
tester
.
widget
(
find
.
byType
(
Column
).
at
(
1
));
expect
(
childrenColumn
.
children
.
length
,
1
);
});
testWidgets
(
'does not show long dividers in insetGrouped section part 2'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
.
insetGrouped
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
// Since the children list is reconstructed with short dividers in it, the
// column retrieved should have 3 items for an input [children] param with 2
// children. Two long dividers, two rows, and one short divider.
final
Column
childrenColumn
=
tester
.
widget
(
find
.
byType
(
Column
).
at
(
1
));
expect
(
childrenColumn
.
children
.
length
,
3
);
});
testWidgets
(
'sets background color for section'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
CupertinoColors
.
systemBlue
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
backgroundColor:
backgroundColor
,
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
final
DecoratedBox
decoratedBox
=
tester
.
widget
(
find
.
byType
(
DecoratedBox
).
first
);
final
BoxDecoration
boxDecoration
=
decoratedBox
.
decoration
as
BoxDecoration
;
expect
(
boxDecoration
.
color
,
backgroundColor
);
});
testWidgets
(
'setting clipBehavior clips children section'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
clipBehavior:
Clip
.
antiAlias
,
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
expect
(
find
.
byType
(
ClipRRect
),
findsOneWidget
);
});
testWidgets
(
'not setting clipBehavior does not clip children section'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
)),
],
),
),
),
);
expect
(
find
.
byType
(
ClipRRect
),
findsNothing
);
});
}
packages/flutter/test/cupertino/list_tile_test.dart
0 → 100644
View file @
0c40945a
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'shows title'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
CupertinoListTile
(
title:
title
,
),
),
),
);
expect
(
tester
.
widget
<
Text
>(
find
.
byType
(
Text
)),
title
);
expect
(
find
.
text
(
'CupertinoListTile'
),
findsOneWidget
);
});
testWidgets
(
'shows subtitle'
,
(
WidgetTester
tester
)
async
{
const
Widget
subtitle
=
Text
(
'CupertinoListTile subtitle'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
CupertinoListTile
(
title:
Icon
(
CupertinoIcons
.
add
),
subtitle:
subtitle
,
),
),
),
);
expect
(
tester
.
widget
<
Text
>(
find
.
byType
(
Text
)),
subtitle
);
expect
(
find
.
text
(
'CupertinoListTile subtitle'
),
findsOneWidget
);
});
testWidgets
(
'shows additionalInfo'
,
(
WidgetTester
tester
)
async
{
const
Widget
additionalInfo
=
Text
(
'Not Connected'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
CupertinoListTile
(
title:
Icon
(
CupertinoIcons
.
add
),
additionalInfo:
additionalInfo
,
),
),
),
);
expect
(
tester
.
widget
<
Text
>(
find
.
byType
(
Text
)),
additionalInfo
);
expect
(
find
.
text
(
'Not Connected'
),
findsOneWidget
);
});
testWidgets
(
'shows trailing'
,
(
WidgetTester
tester
)
async
{
const
Widget
trailing
=
CupertinoListTileChevron
();
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
CupertinoListTile
(
title:
Icon
(
CupertinoIcons
.
add
),
trailing:
trailing
,
),
),
),
);
expect
(
tester
.
widget
<
CupertinoListTileChevron
>(
find
.
byType
(
CupertinoListTileChevron
)),
trailing
);
});
testWidgets
(
'shows leading'
,
(
WidgetTester
tester
)
async
{
const
Widget
leading
=
Icon
(
CupertinoIcons
.
add
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
CupertinoListTile
(
leading:
leading
,
title:
Text
(
'CupertinoListTile'
),
),
),
),
);
expect
(
tester
.
widget
<
Icon
>(
find
.
byType
(
Icon
)),
leading
);
});
testWidgets
(
'sets backgroundColor'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
CupertinoColors
.
systemRed
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
),
backgroundColor:
backgroundColor
,
),
],
),
),
),
);
// Container inside CupertinoListTile is the second one in row.
final
Container
container
=
tester
.
widgetList
<
Container
>(
find
.
byType
(
Container
)).
elementAt
(
1
);
expect
(
container
.
color
,
backgroundColor
);
});
testWidgets
(
'does not change backgroundColor when tapped if onTap is not provided'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
CupertinoColors
.
systemBlue
;
const
Color
backgroundColorActivated
=
CupertinoColors
.
systemRed
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
),
backgroundColor:
backgroundColor
,
backgroundColorActivated:
backgroundColorActivated
,
),
],
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
CupertinoListTile
));
await
tester
.
pump
();
// Container inside CupertinoListTile is the second one in row.
final
Container
container
=
tester
.
widgetList
<
Container
>(
find
.
byType
(
Container
)).
elementAt
(
1
);
expect
(
container
.
color
,
backgroundColor
);
});
testWidgets
(
'changes backgroundColor when tapped if onTap is provided'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
CupertinoColors
.
systemBlue
;
const
Color
backgroundColorActivated
=
CupertinoColors
.
systemRed
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
children:
<
Widget
>[
CupertinoListTile
(
title:
const
Text
(
'CupertinoListTile'
),
backgroundColor:
backgroundColor
,
backgroundColorActivated:
backgroundColorActivated
,
onTap:
()
async
{
await
Future
<
void
>.
delayed
(
const
Duration
(
milliseconds:
1
),
()
{});
},
),
],
),
),
),
);
// Container inside CupertinoListTile is the second one in row.
Container
container
=
tester
.
widgetList
<
Container
>(
find
.
byType
(
Container
)).
elementAt
(
1
);
expect
(
container
.
color
,
backgroundColor
);
// Pump only one frame so the color change persists.
await
tester
.
tap
(
find
.
byType
(
CupertinoListTile
));
await
tester
.
pump
();
// Container inside CupertinoListTile is the second one in row.
container
=
tester
.
widgetList
<
Container
>(
find
.
byType
(
Container
)).
elementAt
(
1
);
expect
(
container
.
color
,
backgroundColorActivated
);
// Pump the rest of the frames to complete the test.
await
tester
.
pumpAndSettle
();
});
testWidgets
(
'does not contain GestureDetector if onTap is not provided'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
children:
const
<
Widget
>[
CupertinoListTile
(
title:
Text
(
'CupertinoListTile'
),
),
],
),
),
),
);
// Container inside CupertinoListTile is the second one in row.
expect
(
find
.
byType
(
GestureDetector
),
findsNothing
);
});
testWidgets
(
'contains GestureDetector if onTap is provided'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListSection
(
children:
<
Widget
>[
CupertinoListTile
(
title:
const
Text
(
'CupertinoListTile'
),
onTap:
()
async
{},
),
],
),
),
),
);
// Container inside CupertinoListTile is the second one in row.
expect
(
find
.
byType
(
GestureDetector
),
findsOneWidget
);
});
testWidgets
(
'resets the background color when navigated back'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
CupertinoColors
.
systemBlue
;
const
Color
backgroundColorActivated
=
CupertinoColors
.
systemRed
;
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Builder
(
builder:
(
BuildContext
context
)
{
final
Widget
secondPage
=
Center
(
child:
CupertinoButton
(
child:
const
Text
(
'Go back'
),
onPressed:
()
=>
Navigator
.
of
(
context
).
pop
<
void
>(),
),
);
return
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoListTile
(
title:
const
Text
(
'CupertinoListTile'
),
backgroundColor:
backgroundColor
,
backgroundColorActivated:
backgroundColorActivated
,
onTap:
()
=>
Navigator
.
of
(
context
).
push
(
CupertinoPageRoute
<
Widget
>(
builder:
(
BuildContext
context
)
=>
secondPage
,
)),
),
),
),
);
},
),
),
);
// Navigate to second page.
await
tester
.
tap
(
find
.
byType
(
CupertinoListTile
));
await
tester
.
pumpAndSettle
();
// Go back to first page.
await
tester
.
tap
(
find
.
byType
(
CupertinoButton
));
await
tester
.
pumpAndSettle
();
// Container inside CupertinoListTile is the second one in row.
final
Container
container
=
tester
.
widget
<
Container
>(
find
.
byType
(
Container
));
expect
(
container
.
color
,
backgroundColor
);
});
group
(
'alignment of widgets for left-to-right'
,
()
{
testWidgets
(
'leading is on the left of title'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
leading
=
Icon
(
CupertinoIcons
.
add
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoListTile
(
title:
title
,
leading:
leading
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getTopLeft
(
find
.
byType
(
Text
));
final
Offset
foundLeading
=
tester
.
getTopRight
(
find
.
byType
(
Icon
));
expect
(
foundTitle
.
dx
>
foundLeading
.
dx
,
true
);
});
testWidgets
(
'subtitle is placed below title and aligned on left'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile title'
);
const
Widget
subtitle
=
Text
(
'CupertinoListTile subtitle'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoListTile
(
title:
title
,
subtitle:
subtitle
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getBottomLeft
(
find
.
text
(
'CupertinoListTile title'
));
final
Offset
foundSubtitle
=
tester
.
getTopLeft
(
find
.
text
(
'CupertinoListTile subtitle'
));
expect
(
foundTitle
.
dx
,
equals
(
foundSubtitle
.
dx
));
expect
(
foundTitle
.
dy
<
foundSubtitle
.
dy
,
isTrue
);
});
testWidgets
(
'additionalInfo is on the right of title'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
additionalInfo
=
Text
(
'Not Connected'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoListTile
(
title:
title
,
additionalInfo:
additionalInfo
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getTopRight
(
find
.
text
(
'CupertinoListTile'
));
final
Offset
foundInfo
=
tester
.
getTopLeft
(
find
.
text
(
'Not Connected'
));
expect
(
foundTitle
.
dx
<
foundInfo
.
dx
,
isTrue
);
});
testWidgets
(
'trailing is on the right of additionalInfo'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
additionalInfo
=
Text
(
'Not Connected'
);
const
Widget
trailing
=
CupertinoListTileChevron
();
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoListTile
(
title:
title
,
additionalInfo:
additionalInfo
,
trailing:
trailing
,
),
),
),
),
);
final
Offset
foundInfo
=
tester
.
getTopRight
(
find
.
text
(
'Not Connected'
));
final
Offset
foundTrailing
=
tester
.
getTopLeft
(
find
.
byType
(
CupertinoListTileChevron
));
expect
(
foundInfo
.
dx
<
foundTrailing
.
dx
,
isTrue
);
});
});
group
(
'alignment of widgets for right-to-left'
,
()
{
testWidgets
(
'leading is on the right of title'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
leading
=
Icon
(
CupertinoIcons
.
add
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
CupertinoListTile
(
title:
title
,
leading:
leading
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getTopRight
(
find
.
byType
(
Text
));
final
Offset
foundLeading
=
tester
.
getTopLeft
(
find
.
byType
(
Icon
));
expect
(
foundTitle
.
dx
<
foundLeading
.
dx
,
true
);
});
testWidgets
(
'subtitle is placed below title and aligned on right'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile title'
);
const
Widget
subtitle
=
Text
(
'CupertinoListTile subtitle'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
CupertinoListTile
(
title:
title
,
subtitle:
subtitle
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getBottomRight
(
find
.
text
(
'CupertinoListTile title'
));
final
Offset
foundSubtitle
=
tester
.
getTopRight
(
find
.
text
(
'CupertinoListTile subtitle'
));
expect
(
foundTitle
.
dx
,
equals
(
foundSubtitle
.
dx
));
expect
(
foundTitle
.
dy
<
foundSubtitle
.
dy
,
isTrue
);
});
testWidgets
(
'additionalInfo is on the left of title'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
additionalInfo
=
Text
(
'Not Connected'
);
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
CupertinoListTile
(
title:
title
,
additionalInfo:
additionalInfo
,
),
),
),
),
);
final
Offset
foundTitle
=
tester
.
getTopLeft
(
find
.
text
(
'CupertinoListTile'
));
final
Offset
foundInfo
=
tester
.
getTopRight
(
find
.
text
(
'Not Connected'
));
expect
(
foundTitle
.
dx
>
foundInfo
.
dx
,
isTrue
);
});
testWidgets
(
'trailing is on the left of additionalInfo'
,
(
WidgetTester
tester
)
async
{
const
Widget
title
=
Text
(
'CupertinoListTile'
);
const
Widget
additionalInfo
=
Text
(
'Not Connected'
);
const
Widget
trailing
=
CupertinoListTileChevron
();
await
tester
.
pumpWidget
(
const
CupertinoApp
(
home:
Center
(
child:
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
CupertinoListTile
(
title:
title
,
additionalInfo:
additionalInfo
,
trailing:
trailing
,
),
),
),
),
);
final
Offset
foundInfo
=
tester
.
getTopLeft
(
find
.
text
(
'Not Connected'
));
final
Offset
foundTrailing
=
tester
.
getTopRight
(
find
.
byType
(
CupertinoListTileChevron
));
expect
(
foundInfo
.
dx
>
foundTrailing
.
dx
,
isTrue
);
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment