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
2e6edaf4
Commit
2e6edaf4
authored
Apr 18, 2019
by
Efthymis Sarmpanis
Committed by
Shi-Hao Hong
Apr 18, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adds Tap Header Feature to ExpansionPanelList (#29390)
parent
beffb248
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
306 additions
and
8 deletions
+306
-8
expansion_panel.dart
packages/flutter/lib/src/material/expansion_panel.dart
+31
-8
strings_en.arb
packages/flutter/res/values/strings_en.arb
+0
-0
expansion_panel_test.dart
packages/flutter/test/material/expansion_panel_test.dart
+275
-0
No files found.
packages/flutter/lib/src/material/expansion_panel.dart
View file @
2e6edaf4
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart';
import
'package:flutter/widgets.dart'
;
import
'expand_icon.dart'
;
import
'ink_well.dart'
;
import
'mergeable_material.dart'
;
import
'theme.dart'
;
...
...
@@ -71,9 +72,11 @@ class ExpansionPanel {
@required
this
.
headerBuilder
,
@required
this
.
body
,
this
.
isExpanded
=
false
,
this
.
canTapOnHeader
=
false
,
})
:
assert
(
headerBuilder
!=
null
),
assert
(
body
!=
null
),
assert
(
isExpanded
!=
null
);
assert
(
isExpanded
!=
null
),
assert
(
canTapOnHeader
!=
null
);
/// The widget builder that builds the expansion panels' header.
final
ExpansionPanelHeaderBuilder
headerBuilder
;
...
...
@@ -88,6 +91,11 @@ class ExpansionPanel {
/// Defaults to false.
final
bool
isExpanded
;
/// Whether tapping on the panel's header will expand/collapse it.
///
/// Defaults to false.
final
bool
canTapOnHeader
;
}
/// An expansion panel that allows for radio-like functionality.
...
...
@@ -109,8 +117,13 @@ class ExpansionPanelRadio extends ExpansionPanel {
@required
this
.
value
,
@required
ExpansionPanelHeaderBuilder
headerBuilder
,
@required
Widget
body
,
bool
canTapOnHeader
=
false
,
})
:
assert
(
value
!=
null
),
super
(
body:
body
,
headerBuilder:
headerBuilder
);
super
(
body:
body
,
headerBuilder:
headerBuilder
,
canTapOnHeader:
canTapOnHeader
,
);
/// The value that uniquely identifies a radio panel so that the currently
/// selected radio panel can be identified.
...
...
@@ -406,6 +419,10 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
items
.
add
(
MaterialGap
(
key:
_SaltedKey
<
BuildContext
,
int
>(
context
,
index
*
2
-
1
)));
final
ExpansionPanel
child
=
widget
.
children
[
index
];
final
Widget
headerWidget
=
child
.
headerBuilder
(
context
,
_isChildExpanded
(
index
),
);
final
Row
header
=
Row
(
children:
<
Widget
>[
Expanded
(
...
...
@@ -415,10 +432,7 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
margin:
_isChildExpanded
(
index
)
?
kExpandedEdgeInsets
:
EdgeInsets
.
zero
,
child:
ConstrainedBox
(
constraints:
const
BoxConstraints
(
minHeight:
_kPanelHeaderCollapsedHeight
),
child:
child
.
headerBuilder
(
context
,
_isChildExpanded
(
index
),
),
child:
headerWidget
,
),
),
),
...
...
@@ -427,7 +441,9 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
child:
ExpandIcon
(
isExpanded:
_isChildExpanded
(
index
),
padding:
const
EdgeInsets
.
all
(
16.0
),
onPressed:
(
bool
isExpanded
)
=>
_handlePressed
(
isExpanded
,
index
),
onPressed:
!
child
.
canTapOnHeader
?
(
bool
isExpanded
)
=>
_handlePressed
(
isExpanded
,
index
)
:
null
,
),
),
],
...
...
@@ -438,7 +454,14 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
key:
_SaltedKey
<
BuildContext
,
int
>(
context
,
index
*
2
),
child:
Column
(
children:
<
Widget
>[
MergeSemantics
(
child:
header
),
MergeSemantics
(
child:
child
.
canTapOnHeader
?
InkWell
(
onTap:
()
=>
_handlePressed
(
_isChildExpanded
(
index
),
index
),
child:
header
,
)
:
header
,
),
AnimatedCrossFade
(
firstChild:
Container
(
height:
0.0
),
secondChild:
child
.
body
,
...
...
packages/flutter/res/values/strings_en.arb
0 → 100644
View file @
2e6edaf4
packages/flutter/test/material/expansion_panel_test.dart
View file @
2e6edaf4
...
...
@@ -5,6 +5,55 @@
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
class
SimpleExpansionPanelListTestWidget
extends
StatefulWidget
{
const
SimpleExpansionPanelListTestWidget
({
Key
key
,
this
.
firstPanelKey
,
this
.
secondPanelKey
,
this
.
canTapOnHeader
=
false
})
:
super
(
key:
key
);
final
Key
firstPanelKey
;
final
Key
secondPanelKey
;
final
bool
canTapOnHeader
;
@override
_SimpleExpansionPanelListTestWidgetState
createState
()
=>
_SimpleExpansionPanelListTestWidgetState
();
}
class
_SimpleExpansionPanelListTestWidgetState
extends
State
<
SimpleExpansionPanelListTestWidget
>
{
List
<
bool
>
extendedState
=
<
bool
>[
false
,
false
];
@override
Widget
build
(
BuildContext
context
)
{
return
ExpansionPanelList
(
expansionCallback:
(
int
_index
,
bool
_isExpanded
)
{
setState
(()
{
extendedState
[
_index
]
=
!
extendedState
[
_index
];
});
},
children:
<
ExpansionPanel
>[
ExpansionPanel
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'B'
:
'A'
,
key:
widget
.
firstPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
canTapOnHeader:
widget
.
canTapOnHeader
,
isExpanded:
extendedState
[
0
],
),
ExpansionPanel
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'D'
:
'C'
,
key:
widget
.
secondPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
canTapOnHeader:
widget
.
canTapOnHeader
,
isExpanded:
extendedState
[
1
],
),
],
);
}
}
void
main
(
)
{
testWidgets
(
'ExpansionPanelList test'
,
(
WidgetTester
tester
)
async
{
int
index
;
...
...
@@ -402,4 +451,230 @@ void main() {
handle
.
dispose
();
});
testWidgets
(
'Ensure canTapOnHeader is false by default'
,
(
WidgetTester
tester
)
async
{
final
ExpansionPanel
_expansionPanel
=
ExpansionPanel
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
=>
const
Text
(
'Demo'
),
body:
const
SizedBox
(
height:
100.0
),
);
expect
(
_expansionPanel
.
canTapOnHeader
,
isFalse
);
});
testWidgets
(
'Toggle ExpansionPanelRadio when tapping header and canTapOnHeader is true'
,
(
WidgetTester
tester
)
async
{
const
Key
firstPanelKey
=
Key
(
'firstPanelKey'
);
const
Key
secondPanelKey
=
Key
(
'secondPanelKey'
);
final
List
<
ExpansionPanel
>
_demoItemsRadio
=
<
ExpansionPanelRadio
>[
ExpansionPanelRadio
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'B'
:
'A'
,
key:
firstPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
value:
0
,
canTapOnHeader:
true
,
),
ExpansionPanelRadio
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'D'
:
'C'
,
key:
secondPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
value:
1
,
canTapOnHeader:
true
,
),
];
final
ExpansionPanelList
_expansionListRadio
=
ExpansionPanelList
.
radio
(
children:
_demoItemsRadio
,
);
await
tester
.
pumpWidget
(
MaterialApp
(
home:
SingleChildScrollView
(
child:
_expansionListRadio
,
),
),
);
// Initializes with all panels closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
firstPanelKey
));
await
tester
.
pumpAndSettle
();
// Now the first panel is open
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsOneWidget
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
secondPanelKey
));
await
tester
.
pumpAndSettle
();
// Now the second panel is open
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsNothing
);
expect
(
find
.
text
(
'D'
),
findsOneWidget
);
});
testWidgets
(
'Toggle ExpansionPanel when tapping header and canTapOnHeader is true'
,
(
WidgetTester
tester
)
async
{
const
Key
firstPanelKey
=
Key
(
'firstPanelKey'
);
const
Key
secondPanelKey
=
Key
(
'secondPanelKey'
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
SingleChildScrollView
(
child:
SimpleExpansionPanelListTestWidget
(
firstPanelKey:
firstPanelKey
,
secondPanelKey:
secondPanelKey
,
canTapOnHeader:
true
,
),
),
),
);
// Initializes with all panels closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
firstPanelKey
));
await
tester
.
pumpAndSettle
();
// The first panel is open
expect
(
find
.
text
(
'A'
),
findsNothing
);
expect
(
find
.
text
(
'B'
),
findsOneWidget
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
firstPanelKey
));
await
tester
.
pumpAndSettle
();
// The first panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
secondPanelKey
));
await
tester
.
pumpAndSettle
();
// The second panel is open
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsNothing
);
expect
(
find
.
text
(
'D'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
byKey
(
secondPanelKey
));
await
tester
.
pumpAndSettle
();
// The second panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
});
testWidgets
(
'Do not toggle ExpansionPanel when tapping header and canTapOnHeader is false'
,
(
WidgetTester
tester
)
async
{
const
Key
firstPanelKey
=
Key
(
'firstPanelKey'
);
const
Key
secondPanelKey
=
Key
(
'secondPanelKey'
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
SingleChildScrollView
(
child:
SimpleExpansionPanelListTestWidget
(
firstPanelKey:
firstPanelKey
,
secondPanelKey:
secondPanelKey
,
),
),
),
);
// Initializes with all panels closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
firstPanelKey
));
await
tester
.
pumpAndSettle
();
// The first panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
secondPanelKey
));
await
tester
.
pumpAndSettle
();
// The second panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
});
testWidgets
(
'Do not toggle ExpansionPanelRadio when tapping header and canTapOnHeader is false'
,
(
WidgetTester
tester
)
async
{
const
Key
firstPanelKey
=
Key
(
'firstPanelKey'
);
const
Key
secondPanelKey
=
Key
(
'secondPanelKey'
);
final
List
<
ExpansionPanel
>
_demoItemsRadio
=
<
ExpansionPanelRadio
>[
ExpansionPanelRadio
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'B'
:
'A'
,
key:
firstPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
value:
0
,
),
ExpansionPanelRadio
(
headerBuilder:
(
BuildContext
context
,
bool
isExpanded
)
{
return
Text
(
isExpanded
?
'D'
:
'C'
,
key:
secondPanelKey
);
},
body:
const
SizedBox
(
height:
100.0
),
value:
1
,
),
];
final
ExpansionPanelList
_expansionListRadio
=
ExpansionPanelList
.
radio
(
children:
_demoItemsRadio
,
);
await
tester
.
pumpWidget
(
MaterialApp
(
home:
SingleChildScrollView
(
child:
_expansionListRadio
,
),
),
);
// Initializes with all panels closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
firstPanelKey
));
await
tester
.
pumpAndSettle
();
// The first panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
secondPanelKey
));
await
tester
.
pumpAndSettle
();
// The second panel is closed
expect
(
find
.
text
(
'A'
),
findsOneWidget
);
expect
(
find
.
text
(
'B'
),
findsNothing
);
expect
(
find
.
text
(
'C'
),
findsOneWidget
);
expect
(
find
.
text
(
'D'
),
findsNothing
);
});
}
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