By default, flutter adds a overscroll effect on ListView/GridView/... on ios
I would like to remove this effect entirely or on one specific scrollable.
What can I do ?
You don't need to do those fancy stuff if you only want to remove overscroll behavior on iOS. Simply, use:
ListView(
physics: ClampingScrollPhysics(),
)
I found this answer (https://stackoverflow.com/a/51119796/5869913) and just added information about deleting overscroll effect.
The overscroll effect comes from BouncingScrollPhysics added by ScrollBehavior
To remove this effect, you need to specify a custom ScrollBehavior and override getScrollPhysics method. For that, simply wrap any given part of your application into a ScrollConfiguration with the desired ScrollBehavior.
The following ScrollBehavior will remove the overscroll effect entirely :
class MyBehavior extends ScrollBehavior {
#override
ScrollPhysics getScrollPhysics(BuildContext context) => ClampingScrollPhysics();
}
You can also remove glow effect with override of method buildViewportChrome like this:
#override
Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) => child;
To remove the overscroll on the whole application, you can add it right under MaterialApp :
MaterialApp(
builder: (context, child) {
return ScrollConfiguration(
behavior: MyBehavior(),
child: child,
);
},
home: MyHomePage(),
);
To remove it on a specific ListView, instead wrap only the desired ListView :
ScrollConfiguration(
behavior: MyBehavior(),
child: ListView(
...
),
)
or just set physics: ClampingScrollPhysics() in the ListView
Related
I have the following simple full code ..
import 'package:flutter/material.dart';
class Profile extends StatefulWidget {
const Profile({ Key? key, }) : super(key: key);
#override
ProfileState createState() => ProfileState();
}
class ProfileState extends State<Profile>{
#override
Widget build(BuildContext context) {
return SafeArea(
child: NestedScrollView(
headerSliverBuilder: (context,value){
return[
const SliverAppBar(
expandedHeight: 400,
)
];
},
body: ListView.builder(
itemCount: 200,
itemBuilder: (BuildContext context, int index) {
return Center(child: Text(index.toString()));
},
)
),
);
}
}
in the previous code, everything is ok and it shifted the scroll in a smooth way BUT when I provide ScrollController into my ListView.builder the scroll is no longer smooth anymore.
so Please How could I keep the first result (with no providing ScrollController) the same as (with providing ScrollController)? .
I recreated your requirements using CustomScrollView, the API is "harder" to use but it allows you implement more complex features (like nested scrollviews as you are doing) because we have direct access to the Slivers API.
You can see that almost any Flutter scrollable widget is a derivation of either CustomScrollView or ScrollView which makes use of Slivers.
NestedScrollView is a subclass of CustomScrollView.
ListView and GridView widgets are subclasses of ScrollView.
Although seems complicated a Sliver is just a portion of a scrollable area:
CustomScrollView(
controller: _scrollController,
slivers: [
const SliverAppBar(expandedHeight: 400),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return Center(child: Text('item $index.'));
},
),
),
],
)
So, instead of creating multiple scrollviews and trying to mix them together (which lead to buggy behaviors), just declare multiple scrollable areas and put them together inside a CustomScrollView, that's it.
See the working example at: https://dartpad.dev/?id=60cb0fa073975f3c80660815ae88af4e.
I have a Flutter scrolling GridView builder that looks like it renders the ListTiles below the Container the GridView is placed in. It appears it renders placeholder grid list tiles even when they are not within the Container bounds.
It looks like for some reason that the GridView ListTiles appear outside (below) the parent container.
In this example the GridView is placed within a Container that is then placed as a child under a parent Scrollbar that is a child widget in a Column. The GridView appears to render the grid ListTiles (renders the background Tile color but not the Tile contents) outside its parent container, appearing as Tiles under the other child widgets of the parent Column widget. See screen grabs below.
Grid List tiles under the Container
When I scroll the list up the shadow Tiles appear in the GridView with the Tile contents.
When I scroll back the Tiles below the GridView appear below?/under? the other widgets in the parent Column. See first image the colored area under the "Bye" text widget.
`
import 'package:flutter/material.dart';
import 'package:scroll5/models/task_model.dart';
/// This is the stateful widget that the main application instantiates.
class ScrollListWidget extends StatefulWidget {
final List<TaskCard> _list;
ScrollListWidget({Key? key})
: _list = [],
super(key: key);
const ScrollListWidget.withList(
List<TaskCard> list, {
Key? key,
}) : _list = list,
super(key: key);
#override
State<ScrollListWidget> createState() => _ScrollListStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _ScrollListStatefulWidgetState extends State<ScrollListWidget> {
#override
Widget build(BuildContext context) {
return Container(height: 240, child: GridView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: widget._list.length,
itemBuilder: (context, index) => WordTile(
widget._list[index].taskName(), widget._list[index].daysEffort()),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 4,
),
));
}
}
class WordTile extends StatelessWidget {
final String word;
final int score;
const WordTile(this.word, this.score, {super.key});
#override
Widget build(BuildContext context) {
return ListTile(
tileColor: Colors.green.withOpacity(.4), title: Text(word));
}
}`
GridView within a Container contained within a Scrollbar and Column
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child:
Column(children: [
Scrollbar(
controller: _scrollController,
thickness: 8,
interactive: true,
thumbVisibility: true,
child:ScrollListWidget.withList(_taskList)
),
Text("Bye",style: TextStyle())])),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add)));
// This trailing comma makes auto-formatting nicer for build methods.
}`
I have a container with a column inside it and its wrapped with a SingleChildScrollView widget.
I want to disable this scroll splash animation that comes when we reach the end of the scroll view.
This is what my widget tree looks like for this one.
Container - with rounded border
|
SingleChildScrollView
|
Column
|
Some widgets in children
Because this comes over the rounded border of the container I want to disable it.
Apoorv, google has many solutions to this question, please check this page for a solution to your problem.
try the following
MaterialApp(
builder: (context, child) {
return ScrollConfiguration(
behavior: MyBehavior(),
child: child,
);
},
home: new MyHomePage(),
);
and define custom scroll behavior like this
class MyBehavior extends ScrollBehavior {
#override
Widget buildViewportChrome(
BuildContext context, Widget child, AxisDirection axisDirection) {
return child;
}
}
I have made PageView, set physics to NeverScrollableScrollPhysics to disabled scroll and implements it by buttons. But when I call controller.animateToPage(/* ... */), my PageView animated with scroll glow. How to disabled this behaviour? Or this is bug of Flutter itself?
To disable Scroll glow, create a class that extends ScrollBehavior
class DisableScrollGlowBehavior extends ScrollBehavior {
#override
Widget buildViewportChrome(
BuildContext context, Widget child, AxisDirection axisDirection) {
return child;
}
}
Then wrap your scrollable List widget with ScrollConfiguration widget using the behavior of the class created.
ScrollConfiguration(
behavior: DisableScrollGlowBehavior(),
child: ListView(
children: [],
),
)
I have a page in my app with a ListView. I'm using a StreamBuilder to receive data. When there is data I build the listview children and the listview is wrapped inside a RefreshIndicator. This is working fine.
Now I want to handle the situation when there is no data aka EmptyState. Be able to display a Container with some content, while still be able to support pull to refresh to force the page/Listview to check for data.
This is how I'm currently doing it
return RefreshIndicator(
key: upcomingRefreshIndicatorKey,
onRefresh: handleRefresh,
child: snapshot.hasData ? list : Stack(children: <Widget>[list, emptystate],) ,
);
I'm not sure this is the way to handle EmptyState with ListView, but so far this is the only way I can see to still support refresh when the list is empty. So my main question is how you best handle empty state with LIstView and still supporting PullToRefresh?
To support the RefreshIndicator inside an empty ListView you can set physics: AlwaysScrollableScrollPhysics(). This makes the ListView always scrolalble. Therefore, we can always trigger the RefreshIndicator.
The Container you want to display when the ListView is empty can be accomplished by two methods:
If your data is empty (or null), replace the ListView with a SingleChildScrollView with physics set to AlwaysScrollableScrollPhysics(). Then you can place your Container as the child of the SingleChildScrollView.
Instead of replacing the ListView you can simply replace the children with your Container, given your data is empty (or null).
In the follwing standalone example, I took the second approach:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
// dummy stream
StreamController<List<String>> yourStream = StreamController<List<String>>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: RefreshIndicator(
onRefresh: refresh,
child: StreamBuilder<List<String>>(
stream: yourStream.stream,
builder: (_, snapshot) => ListView(
physics: AlwaysScrollableScrollPhysics(), // This makes it always scrollable!
children: snapshot.data == null
? [Text('Nothing here :(')]
: snapshot.data
.map((text) => ListTile(
title: Text(text),
))
.toList()),
),
),
),
),
);
}
Future<void> refresh() {
// dummy code
yourStream.add(['Quick', 'Brown', 'Fox']);
return Future.value();
}
}