I have this design:
CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Text("sds"),
],
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('Grid Item $index'),
);
},
childCount: 10,
),
),
SliverFillRemaining(
child: ReorderableListView(
scrollController: _scrollController,
children: <Widget>[
for (final items in item)
Card(
color: Colors.blueGrey,
key: ValueKey(items),
elevation: 2,
child: ListTile(
title: Text(items),
leading: Icon(
Icons.work,
color: Colors.black,
),
),
),
],
onReorder: reorderData,
),
),
],
),
Everything between the SliverAppBar and the ReorderableListView is just an example of how it should work.
I need to order some elements so for that I use a ReorderableListView but this needs to be inside a CustomScrollView and using its scroll. Right now everything works except for the scroll. The SliverAppBar minimises when scrolling over the grid, but the ReorderableListView has it owns scroll.
Is there any way to cancel the second scroll and having these 2 elements working together?
I also uses this library https://pub.dev/packages/reorderables but if you have a look to the repo it seems abandoned (and I faced different issues)
Related
I am trying to implement the scrolling effect on the home screen on whatsap on iphone, where the search bar disappears first while scrolling the the rest of the scrolling takes place. Any idea on how to implement it ?
You can try using the SliverAppBar with a CustomScrollView.
return Scaffold(
//1
body: CustomScrollView(
slivers: <Widget>[
//2
SliverAppBar(
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Goa', textScaleFactor: 1),
background: Image.asset(
'assets/images/beach.png',
fit: BoxFit.fill,
),
),
),
//3
SliverList(
delegate: SliverChildBuilderDelegate(
(_, int index) {
return ListTile(
leading: Container(
padding: EdgeInsets.all(8),
width: 100,
child: Placeholder()),
title: Text('Place ${index + 1}', textScaleFactor: 2),
);
},
childCount: 20,
),
),
],
),
);
So basically I am using a NestedScrollView which has a TabBarView as its body. My setup works pretty much as desired however there is a stutter while scrolling. When the TabBar reaches the top of the page/touches the bottom of the SliverAppBar while scrolling, there is a brief pause in scrolling before scrolling is resumed as normal. This pause also happens when we scroll back down.
Here is the error:
I cannot seem to figure out how to fix this pause. It is brief yet annoyingly noticeable. How could I fix this?
Thank you!
You can use only one CustomScrollView in this case. and For inner scrollable physics: NeverScrollableScrollPhysics(),
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
const SliverAppBar(
pinned: true,
title: Text('AppBar'),
collapsedHeight: 100,
backgroundColor: Colors.blue,
),
SliverToBoxAdapter(
child: Container(
alignment: Alignment.center,
height: 100,
color: Colors.redAccent,
child: const Text('Container'),
),
),
SliverPinnedHeader(
child: Container(
color: Colors.white,
child: TabBar(
controller: _tabController,
tabs: const [
Tab(icon: Icon(Icons.shopping_cart, color: Colors.black)),
Tab(icon: Icon(Icons.bookmark, color: Colors.black)),
],
),
),
),
SliverFillRemaining(
child: TabBarView(
// physics: NeverScrollableScrollPhysics(),
controller: _tabController,
children: [
ListView.builder(
itemCount: 44,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
tileColor: Colors.pinkAccent,
title: Text('index $index'),
);
},
),
ListView.builder(
itemCount: 44,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
tileColor: Colors.pinkAccent,
title: Text('index $index'),
);
},
),
],
)),
],
));
}
I'm building my first Flutter app and I have some performance issues.
I have a screen with a CustomScrollView, to especially get a custom AppBar, with a Hero image. Next, I have some fields/filters and then I have my GridView.builder because I can have between 25 to 400 elements in this list, and I want to build them only when the user is scrolling.
But after checking, I saw that all of my elements (400) are built immediately, and makes the app very laggy.
Can you please tell me what I've done wrong?
EDIT: You can test it via this Pastebin: Just simply copy-paste it into a new flutter project https://pastebin.com/xhd9CdUp
Basically, this is the structure of my code:
CustomScrollView
SliverAppBar
SliverPadding
SliverChildListDelegate
SearchField
SizedBox
ValueListenableBuilder // hive package
GridView.builder
CardWithMenuWidget(index)
And here is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
body: SafeArea(
child: LayoutBuilder(builder: (context, constraints) {
return CustomScrollView(
slivers: [
SliverAppBar(
elevation: 0,
backgroundColor: Colors.transparent,
expandedHeight: 300,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.symmetric(vertical: 4),
title: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: constraints.maxWidth * 0.5),
child: Hero(
tag: "extension-logo-${extension.uuid}",
child: CachedImage(imageUrl: extension.logoMediumUrl!),
),
),
background: Align(
alignment: Alignment.topCenter,
child: Hero(
tag: "extension-spotlight-${extension.uuid}",
child: CachedImage(
imageUrl: extension.spotlightMediumUrl!,
fit: BoxFit.fitWidth,
height: 240,
width: double.infinity,
alignment: Alignment.topCenter,
),
),
),
),
),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
sliver: SliverList(
delegate: SliverChildListDelegate(
[
SearchField(
controller: this._searchController,
labelText: "Search into ${extension.name}",
cancelText: 'Cancel',
),
SizedBox(height: 32),
// Dynamic item list
ValueListenableBuilder(
valueListenable:
ExtensionBox.box.listenable(keys: [_uuid]),
builder: (context, Box<Extension> box, child) {
var cardUuidList = box.get(_uuid)!.cards?.keys;
return _buildCardListGrid(cardUuidList);
},
),
FakeBottomBarPadding(),
],
),
),
),
],
);
}),
),
);
}
Widget _buildCardListGrid(cardUuidList) {
return LoadingContainer(
isLoading: _isSyncing,
child: cardUuidList == null
? Center(
child: Text('No data.'),
)
: GridView.builder(
addAutomaticKeepAlives: true,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: cardUuidList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 16.0,
crossAxisSpacing: 16.0,
childAspectRatio: CardModel.ratio,
),
itemBuilder: (context, cardIndex) {
String cardUuid = cardUuidList.elementAt(cardIndex);
return CardWithMenuWidget(
uuid: cardUuid,
);
},
),
);
}
Thank you!
My solution, based on my pastebin code:
CustomScrollView(
SliverAppBar(),
...
ValueListenableBuilder(
valueListenable: _list,
builder: (context, listBuilder, child) {
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 16.0,
crossAxisSpacing: 16.0,
childAspectRatio: 635 / 889,
),
delegate: SliverChildBuilderDelegate(
(c, i) {
return ItemWidget(listBuilder[i]);
},
childCount: _list.value.length,
),
);
},
),
)
So I have a CustomScrollView with a SliverAppBar(floating: true, snap: true), a SliverPersistentHeader(pinned: true) a SliverList.
What I am trying to do is to click a button in the header and just pop a widget in the space taken by the SliverPersistentHeade and SliverList. I tried putting them inside a nestedScrollView and nesting SliverPersistentHeader and SliverList inside another scrollView but since the SliverAppBar and SliverPersistentHeader are not together it messes up the supposed effect when it(SliverAppBar) snaps up and off.
Why do I need it like that: I want to perform some animations with the elements in the appBar as the lower part transitions between the two pages kinda like animtions shared axis package
problems
keep the persistantHeader on the first page
keep the snap floating appbar feature
transition the lower part
Note: Answers don't necessary need to depend on slivers. a possible way to achieve that will do
My code at the moment looks roughly like this
CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: [
SliverAppBar(
backgroundColor: Colors.white,
floating: true,
snap: true,
titleSpacing: 0,
elevation: 4,
title: Row(
children: [
Expanded(
child: Container(
padding: EdgeInsets.only(top: 10, bottom: 10, left: 10),
child: Container(color: Colors.grey),
),
),
),
Container(
child: LeadingButton(
color: lightShadeColor,
icon: Icons.qr_code_scanner_rounded,
iconColor: darkColor,
size: 37, // btnShadow: false
),
),
],
),
),
SliverPersistentHeader(
delegate: PersistentHeader(
widget: Row(
// Format this to meet your need
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Text('data'),
)
],
),
),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
title: Text('index $index'),
);
}),
)
],
),
Is it possible to borderRadius to SliverList / SliverGrid? If yes, how to radius topRight and topLeft
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
),
),
SliverGrid(
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Colors.teal[100 * (index % 9)],
alignment: Alignment.center,
child: Text('grid item $index'),
);
},
childCount: 120,
),
),
],
),
You might not be able to make a SliverList rounded (even in some part), but you can tweak your way through by putting the children of the list in a Column and wrapping the Column in a Container then setting the borderRadius of the Container.
And the Scrolling still works perfectly as a normal CustomScrollView
The whole idea is to make the Column work for the SliverList. So you have to set childCount to 1 and allow the Column handle everything about the children
You can try the same for SliverGrid
I wrote an example with SliverList
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text("Hey"),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, __){
return Container(
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10))
),
child: Column(
children: List.generate(10, (index) => Container(child: ListTile(title: Text("$index nothing")))),
),
);
},
childCount: 1
),
)
],
),
);
}
}
I hope this helps you