Last active
April 25, 2020 14:34
-
-
Save liudonghua123/b6c999910fe150e85956177514de90ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:math' as math; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: Scaffold( | |
body: SamplePage(), | |
), | |
); | |
} | |
} | |
class SamplePage extends StatefulWidget { | |
SamplePage({Key key}) : super(key: key); | |
@override | |
_SamplePageState createState() => _SamplePageState(); | |
} | |
class _SamplePageState extends State<SamplePage> | |
with SingleTickerProviderStateMixin { | |
@override | |
void initState() { | |
super.initState(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: CustomScrollView( | |
slivers: <Widget>[ | |
SliverAppBar( | |
actions: <Widget>[ | |
Material( | |
color: Colors.transparent, | |
child: InkWell( | |
onTap: () {}, | |
child: IconButton( | |
icon: Icon(Icons.access_alarm), | |
onPressed: () {}, | |
), | |
), | |
), | |
], | |
title: Text('SliverAppBar'), | |
backgroundColor: Theme.of(context).accentColor.withOpacity(0.5), | |
expandedHeight: 200.0, | |
flexibleSpace: FlexibleSpaceBar( | |
background: Container( | |
child: FittedBox( | |
fit: BoxFit.contain, child: Text('SliverAppBar.flexibleSpace')), | |
color: Colors.purple, | |
), | |
), | |
bottom: PreferredSize( | |
child: Container( | |
width: MediaQuery.of(context).size.width, | |
height: 50, | |
color: Colors.red.withOpacity(0.3), | |
child: FittedBox(fit: BoxFit.contain, child: Text('SliverAppBar.bottom')), | |
), | |
preferredSize: Size.fromHeight(50), | |
), | |
// floating: floating, | |
// snap: snap, | |
pinned: true, | |
), | |
SliverGrid.extent( | |
children: List.generate(30, (int index) { | |
return Card( | |
elevation: 5, | |
child: Center(child: Text('item ${index.toString()}')), | |
); | |
}), | |
maxCrossAxisExtent: 100.0, | |
), | |
SliverPersistentHeader( | |
pinned: false, | |
floating: true, | |
delegate: _SliverAppBarDelegate( | |
minHeight: 60.0, | |
maxHeight: 180.0, | |
child: Container( | |
child: FittedBox(fit: BoxFit.contain, child: Text('SliverPersistentHeader')), | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
colors: [Colors.green, Colors.orange], | |
), | |
), | |
), | |
), | |
), | |
SliverList( | |
delegate: SliverChildBuilderDelegate( | |
(BuildContext context, int index) { | |
if (index > 30) { | |
return null; | |
} | |
return ListTile( | |
title: Text('item ${index.toString()}'), | |
onTap: () {}, | |
); | |
}, | |
), | |
// delegate: SliverChildListDelegate( | |
// List.generate(30, (int index) { | |
// return ListTile(title: Text('item ${index.toString()}'),onTap: (){},); | |
// }), | |
// ), | |
), | |
], | |
), | |
); | |
} | |
} | |
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { | |
_SliverAppBarDelegate({ | |
@required this.minHeight, | |
@required this.maxHeight, | |
@required this.child, | |
}); | |
final double minHeight; | |
final double maxHeight; | |
final Widget child; | |
@override | |
double get minExtent => minHeight; | |
@override | |
double get maxExtent => math.max(maxHeight, minHeight); | |
@override | |
Widget build( | |
BuildContext context, double shrinkOffset, bool overlapsContent) { | |
return new SizedBox.expand(child: child); | |
} | |
@override | |
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) { | |
return maxHeight != oldDelegate.maxHeight || | |
minHeight != oldDelegate.minHeight || | |
child != oldDelegate.child; | |
} | |
} |
https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html
try to fix pin issue https://juejin.im/post/5bea43ade51d45544844010a
NOT FINISHED!
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SamplePage(),
),
);
}
}
class SamplePage extends StatefulWidget {
SamplePage({Key key}) : super(key: key);
@override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage>
with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
actions: <Widget>[
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {},
child: IconButton(
icon: Icon(Icons.access_alarm),
onPressed: () {},
),
),
),
],
title: Text('SliverAppBar'),
backgroundColor: Theme.of(context).accentColor.withOpacity(0.5),
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
background: Container(
child: FittedBox(
fit: BoxFit.contain,
child: Text('SliverAppBar.flexibleSpace')),
color: Colors.purple,
),
),
bottom: PreferredSize(
child: Container(
width: MediaQuery.of(context).size.width,
height: 50,
color: Colors.red.withOpacity(0.3),
child: FittedBox(
fit: BoxFit.contain, child: Text('SliverAppBar.bottom')),
),
preferredSize: Size.fromHeight(50),
),
// floating: floating,
// snap: snap,
pinned: true,
),
),
SliverOverlapInjector(
// This is the flip side of the SliverOverlapAbsorber above.
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverGrid.extent(
children: List.generate(30, (int index) {
return Card(
elevation: 5,
child: Center(child: Text('item ${index.toString()}')),
);
}),
maxCrossAxisExtent: 100.0,
),
SliverPersistentHeader(
pinned: false,
floating: true,
delegate: _SliverAppBarDelegate(
minHeight: 60.0,
maxHeight: 180.0,
child: Container(
child: FittedBox(
fit: BoxFit.contain, child: Text('SliverPersistentHeader')),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.green, Colors.orange],
),
),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index > 30) {
return null;
}
return ListTile(
title: Text('item ${index.toString()}'),
onTap: () {},
);
},
),
// delegate: SliverChildListDelegate(
// List.generate(30, (int index) {
// return ListTile(title: Text('item ${index.toString()}'),onTap: (){},);
// }),
// ),
),
],
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({
@required this.minHeight,
@required this.maxHeight,
@required this.child,
});
final double minHeight;
final double maxHeight;
final Widget child;
@override
double get minExtent => minHeight;
@override
double get maxExtent => math.max(maxHeight, minHeight);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new SizedBox.expand(child: child);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
problem demo on https://juejin.im/post/5bea43ade51d45544844010a
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new SamplePage(),
));
}
class SamplePage extends StatefulWidget {
@override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> {
@override
Widget build(BuildContext context) {
return Scaffold(body: getBody());
}
Widget getBody() {
return new NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
var widgets = <Widget>[
new SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverAppBar(
actions: <Widget>[
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {},
child: IconButton(
icon: Icon(Icons.access_alarm),
onPressed: () {},
),
),
),
],
title: Text('SliverAppBar'),
backgroundColor: Theme.of(context).accentColor.withOpacity(0.5),
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
background: Container(
child: FittedBox(
fit: BoxFit.contain,
child: Text('SliverAppBar.flexibleSpace')),
color: Colors.purple,
),
),
bottom: PreferredSize(
child: Container(
width: MediaQuery.of(context).size.width,
height: 50,
color: Colors.red.withOpacity(0.3),
child: FittedBox(
fit: BoxFit.contain,
child: Text('SliverAppBar.bottom')),
),
preferredSize: Size.fromHeight(50),
),
// floating: floating,
// snap: snap,
pinned: true,
forceElevated: innerBoxIsScrolled,
),
),
// new SliverOverlapAbsorber(
// handle:
// NestedScrollView.sliverOverlapAbsorberHandleFor(context),
// child:
SliverPersistentHeader(
pinned: true,
floating: false,
delegate: _SliverPersistentHeaderDelegate(
Container(
color: Color.fromARGB(100, 100, 100, 100),
height: 100.0,
child: Center(
child: Text("Pinned wiget"),
)),
100.0))
// ),
];
return widgets;
},
body: SafeArea(
top: false,
bottom: false,
child: new Builder(
// This Builder is needed to provide a BuildContext that is "inside"
// the NestedScrollView, so that sliverOverlapAbsorberHandleFor() can
// find the NestedScrollView.
builder: (BuildContext context) {
return new CustomScrollView(
// The "controller" and "primary" members should be left
// unset, so that the NestedScrollView can control this
// inner scroll view.
// If the "controller" property is set, then this scroll
// view will not be associated with the NestedScrollView.
// The PageStorageKey should be unique to this ScrollView;
// it allows the list to remember its scroll position when
// the tab view is not on the screen.
// key: new PageStorageKey<String>("KeyName$name"),
slivers: <Widget>[
new SliverOverlapInjector(
// This is the flip side of the SliverOverlapAbsorber above.
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
),
new SliverPadding(
padding: const EdgeInsets.all(8.0),
// In this example, the inner scroll view has
// fixed-height list items, hence the use of
// SliverFixedExtentList. However, one could use any
// sliver widget here, e.g. SliverList or SliverGrid.
sliver: new SliverFixedExtentList(
// The items in this example are fixed to 48 pixels
// high. This matches the Material Design spec for
// ListTile widgets.
itemExtent: 48.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
// This builder is called for each child.
// In this example, we just number each list item.
return Container(
color: Colors.red,
height: 48.0,
width: double.infinity,
child: Text("Index$index"),
);
},
// The childCount of the SliverChildBuilderDelegate
// specifies how many children this inner list
// has. In this example, each tab has a list of
// exactly 30 items, but this is arbitrary.
childCount: 2,
),
),
),
],
);
})));
}
}
class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final double height;
final Widget widget;
_SliverPersistentHeaderDelegate(this.widget, this.height);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
// TODO: implement build
return widget;
}
// TODO: implement maxExtent
@override
double get maxExtent => height;
// TODO: implement minExtent
@override
double get minExtent => height;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
// TODO: implement shouldRebuild
return false;
}
}
More clear explanation code.
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart' hide NestedScrollView;
void main() {
runApp(MaterialApp(
home: SamplePage(),
));
}
class SamplePage extends StatefulWidget {
@override
_SamplePageState createState() => _SamplePageState();
}
class _SamplePageState extends State<SamplePage> {
@override
Widget build(BuildContext context) {
return Scaffold(body: getBody());
}
Widget getBody() {
return NestedScrollView(
pinnedHeaderSliverHeightBuilder: () {
return MediaQuery.of(context).padding.top +
AppBar().preferredSize.height +
// bottom
50 +
// pinned widget
100;
},
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
var widgets = <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverAppBar(
actions: <Widget>[
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {},
child: IconButton(
icon: Icon(Icons.access_alarm),
onPressed: () {},
),
),
),
],
title: Text('SliverAppBar'),
backgroundColor: Theme.of(context).accentColor.withOpacity(0.5),
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
background: Container(
child: FittedBox(
fit: BoxFit.contain,
child: Text('SliverAppBar.flexibleSpace')),
color: Colors.purple,
),
),
bottom: PreferredSize(
child: Container(
width: MediaQuery.of(context).size.width,
height: 50,
color: Colors.red.withOpacity(0.3),
child: FittedBox(
fit: BoxFit.contain,
child: Text('SliverAppBar.bottom')),
),
preferredSize: Size.fromHeight(50),
),
// floating: floating,
// snap: snap,
pinned: true,
forceElevated: innerBoxIsScrolled,
),
),
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverPersistentHeader(
pinned: true,
delegate: _SliverPersistentHeaderDelegate(
Container(
color: Color.fromARGB(100, 100, 100, 100),
height: 100.0,
child: Center(
child: Text("Pinned wiget"),
)),
100.0),
),
// ),
];
return widgets;
},
body: SafeArea(
top: false,
bottom: false,
child: Builder(
// This Builder is needed to provide a BuildContext that is "inside"
// the NestedScrollView, so that sliverOverlapAbsorberHandleFor() can
// find the NestedScrollView.
builder: (BuildContext context) {
return CustomScrollView(
// The "controller" and "primary" members should be left
// unset, so that the NestedScrollView can control this
// inner scroll view.
// If the "controller" property is set, then this scroll
// view will not be associated with the NestedScrollView.
// The PageStorageKey should be unique to this ScrollView;
// it allows the list to remember its scroll position when
// the tab view is not on the screen.
// key: PageStorageKey<String>("KeyName$name"),
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index > 5) {
return null;
}
return ListTile(
title: Text('item ${index.toString()}'),
onTap: () {},
);
},
),
),
SliverPersistentHeader(
pinned: true,
delegate: _SliverPersistentHeaderDelegate(
Container(
color: Color.fromARGB(100, 100, 100, 100),
height: 100.0,
child: Center(
child: Text("body Pinned wiget 1"),
)),
100.0),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index > 5) {
return null;
}
return ListTile(
title: Text('item ${index.toString()}'),
onTap: () {},
);
},
),
),
SliverPersistentHeader(
pinned: true,
delegate: _SliverPersistentHeaderDelegate(
Container(
color: Color.fromARGB(100, 100, 100, 100),
height: 100.0,
child: Center(
child: Text("body Pinned wiget 2"),
)),
100.0),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index > 15) {
return null;
}
return ListTile(
title: Text('item ${index.toString()}'),
onTap: () {},
);
},
),
),
],
);
})));
}
}
class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
final double height;
final Widget widget;
_SliverPersistentHeaderDelegate(this.widget, this.height);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
// TODO: implement build
return widget;
}
// TODO: implement maxExtent
@override
double get maxExtent => height;
// TODO: implement minExtent
@override
double get minExtent => height;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
// TODO: implement shouldRebuild
return false;
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://dartpad.cn/b6c999910fe150e85956177514de90ce