how to Prevent Listview from ever scrolling between certain bounds? - flutter

I've a initialScrollOffset in my ScrollController set to 100. This allows the top of my ListView in my Scaffold body to appear under my appBar. (Desired behaviour, my appbar is transparent). This works fine when the page first loads but I'd like my ScrollController to prevent my listView from ever scrolling between 0 and 100 and always have an initialScrollOffset of 100. ScrollController has a boolean property keepScrollOffset` but that doesn't prevent the scroll going from 0-100. Here is my code so far:
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
ScrollController _scrollController;
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: Container(
color: Colors.blue.withOpacity(0.25),
child: ListView.builder(
itemCount: 30,
itemBuilder: (context, index) {
return Container(
color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.5),
child: ListTile(
title: Text('Item $index'),
),
);
},
controller: _scrollController,
),
),
);
}
#override
void initState() {
_scrollController = ScrollController(initialScrollOffset: 100, keepScrollOffset: true)
..addListener(() {
print("offset = ${_scrollController.offset}");
});
super.initState();
}
}
void main() {
runApp(MaterialApp(home: HomePage()));
}

If you want to add padding to the inner content that scrolls up and down with the items, you can just add padding to the ListView.
padding: EdgeInsets.only(top: 100)
Not necessary to use ScrollController.

Related

Issue with nested scroll when both scroll are always visible (Flutter Stable Channel)

The issue is on web when we try to scroll the vertically scrollable widget which is a child of horizontally scrollable widget where scroll bar is always visible.
When we scroll the horizontally scrollable widget the scroll bar for horizontal scroll is correctly displayed as shown in picture
horizontal scroll
But when we scroll the vertically scrollable widget the horizontal scrollbar disappears and new vertical scroll bar is seen at right as shown in screenshot
vertical scroll
Code to reproduce the issue.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scroll Issue Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Scroll Issue Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _horizontalController = ScrollController();
final List<ScrollController> _verticalController = [];
#override
void initState() {
super.initState();
for (int i = 0; i < 6; i++) {
_verticalController.add(ScrollController());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Row(
children: <Widget>[
Expanded(
child: _buildHorizontalScroll(),
)
],
),
);
}
Widget _buildHorizontalScroll() {
return Scrollbar(
isAlwaysShown: true,
controller: _horizontalController,
notificationPredicate: (predicate) {
return predicate.depth == 0;
},
child: ListView.separated(
controller: _horizontalController,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => Container(
margin: const EdgeInsets.all(30),
width: 300,
color: Colors.black12,
child: Column(
children: [
Expanded(
child: _buildVerticalScroll(index),
)
],
),
),
separatorBuilder: (context, index) => const Divider(
height: 30,
),
itemCount: 6),
);
}
Widget _buildVerticalScroll(int index) {
return Scrollbar(
scrollbarOrientation: ScrollbarOrientation.right,
isAlwaysShown: true,
controller: _verticalController[index],
child: ListView.separated(
controller: _verticalController[index],
itemBuilder: (context, index) => Container(
margin: const EdgeInsets.all(30),
height: 100,
color: Colors.green,
),
separatorBuilder: (context, index) => const Divider(
height: 30,
),
itemCount: 200),
);
}
}
Checking the notification predicate in the horizontal scroll bar, it detects the depth of scroll as both 0 and 1 when scrolling the vertically scrollable list. Is there a workaround for this problem? The problem started to appear since the upgrade to 2.5.0 for stable channel of flutter.
Maybe this package will help you easily customize your thumb and scrolling track both horizontally and vertically
https://pub.dev/packages/cross_scroll

Can't access scrollcontroller in Listview under SingleChildScrollView

I'm unable to access the scrollContoller in the ListView.builder. I think its because it's below SingleChildScrollView (when I remove SingleChildScrollView I start getting print statements from the listener attached to the scrollController ). I need to access the offset from scrollController in the ListView.builder but not sure how to do so. Here's a minimum example. How would I get the listener printing from the controller in the listView..
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
ScrollController scrollController = ScrollController();
#override
void initState() {
scrollController.addListener(() {
print("Why am I not getting print statements....");
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 400,
color: Colors.orange,
),
ListView.builder(
controller: scrollController,
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: 30,
itemBuilder: (context, index) {
return Container(
color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.5),
child: ListTile(
title: Text('Item $index'),
),
);
},
),
],
),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage()));
}

Is it possible to have any widget like this in flutter

Is there any built in widget in flutter to implement a widget like(red border in picture below) ?
which we can drag it change the value.
That's simply a horizontal ListView with predefined height.
SizedBox(
height: 56, // height of ListView
child: ListView.builder(
scrollDirection: Axis.horizontal, // makes it scroll in horizontal direction
itemBuilder: (_, index) {
// build your years
},
),
)
The desired output can be brought using the ListView.
Flutter just uploaded a video on ListView in which you can find your answer:
https://www.youtube.com/watch?v=KJpkjHGiI5A
Example code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
bool change ;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int i=0;
void fxn(){
setState(() {
i++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('List View'),),
body: ListView.builder(
itemCount: 30,
itemBuilder: (_, index)=>Padding(
padding: const EdgeInsets.fromLTRB(0, 270, 0, 270),
child: MaterialButton(
child:
Text('200$index',style: TextStyle(fontSize: 25),),
onPressed: (){
},
color: Colors.white70,
),
),
scrollDirection: Axis.horizontal,
)
);
}
}
Output: Output

Allow GridView to overlap SliverAppBar

I am trying to reproduce the following example from the earlier Material design specifications (open for animated demo):
Until now I was able to produce the scrolling effect, but the overlap of the content is still missing. I couldn't find out how to do this properly.
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('Title'),
expandedHeight: 200.0,
primary: true,
pinned: true,
),
SliverFixedExtentList(
itemExtent: 30.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int i) => Text('Item $i')
),
),
],
),
);
}
}
I managed to get this functionality, using the ScrollController and a couple of tricks:
Here's the code:
ScrollController _scrollController;
static const kHeaderHeight = 235.0;
double get _headerOffset {
if (_scrollController.hasClients) if (_scrollController.offset > kHeaderHeight)
return -1 * (kHeaderHeight + 50.0);
else
return -1 * (_scrollController.offset * 1.5);
return 0.0;
}
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(() => setState(() {}));
}
#override
Widget build(BuildContext context) {
super.build(context);
return StackWithAllChildrenReceiveEvents(
alignment: AlignmentDirectional.topCenter,
children: [
Positioned(
top: _headerOffset,
child: Container(
height: kHeaderHeight,
width: MediaQuery.of(context).size.width,
color: Colors.blue,
),
),
Padding(
padding: EdgeInsets.only(left: 20.0, right: 20.0),
child: Feed(controller: _scrollController, headerHeight: kHeaderHeight),
),
],
);
}
To make the Feed() not overlap the blue container, I simply made the first child of it a SizedBox with the required height property.
Note that I am using a modified Stack class. That is in order to let the first Widget in the stack (the blue container) to detect presses, so it will fit my uses; unfortunately at this point the default Stack widget has an issue with that, you can read more about it over https://github.com/flutter/flutter/issues/18450.
The StackWithAllChildrenReceiveEvents code can be found over https://github.com/flutter/flutter/issues/18450#issuecomment-575447316.
I had the same problem and could not solve it with slivers. This example from another stackoverflow question solved my problem.
flutter - App bar scrolling with overlapping content in Flexible space
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Scroll demo',
home: new Scaffold(
appBar: new AppBar(elevation: 0.0),
body: new CustomScroll(),
),
);
}
}
class CustomScroll extends StatefulWidget {
#override
State createState() => new CustomScrollState();
}
class CustomScrollState extends State<CustomScroll> {
ScrollController scrollController;
double offset = 0.0;
static const double kEffectHeight = 100.0;
#override
Widget build(BuildContext context) {
return new Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget> [
new Container(
color: Colors.blue,
height: (kEffectHeight - offset * 0.5).clamp(0.0, kEffectHeight),
),
new Positioned(
child: new Container(
width: 200.0,
child: new ListView.builder(
itemCount: 100,
itemBuilder: buildListItem,
controller: scrollController,
),
),
),
],
);
}
Widget buildListItem(BuildContext context, int index) {
return new Container(
color: Colors.white,
child: new Text('Item $index')
);
}
void updateOffset() {
setState(() {
offset = scrollController.offset;
});
}
#override
void initState() {
super.initState();
scrollController = new ScrollController();
scrollController.addListener(updateOffset);
}
#override
void dispose() {
super.dispose();
scrollController.removeListener(updateOffset);
}
}
Change the list to a grid and its what you want

flutter - App bar scrolling with overlapping content in Flexible space

i am trying to recreate App bar scrolling with overlapping content in Flexible space using flutter.
the behavior is demonstrated here:
http://karthikraj.net/2016/12/24/scrolling-behavior-for-appbars-in-android/
I created collapsing AppBar using SliverAppBar already, using the code I pasted here, I am trying to create THIS
i cant use Stack for it because i cant find any onScroll callback, so far i created appbar with flexibleSpace, the app bar collapse on scroll:
Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[
SliverAppBar(
forceElevated: innerBoxIsScrolled,
pinned: true,
expandedHeight: 180.0,
),
],
body: ListView.builder(
itemCount: 30,
itemBuilder: (context, index) => Text(
"Item $index",
style: Theme.of(context).textTheme.display1,
),
),
),
);
edit: Example of what i want to create
ScrollViews take a ScrollController which is a Listenable that notifies on scroll offset updates.
You can listen to the ScrollController and use a Stack to achieve the effect you're interested in based on the scroll offset.
Here's a quick example:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Scroll demo',
home: new Scaffold(
appBar: new AppBar(elevation: 0.0),
body: new CustomScroll(),
),
);
}
}
class CustomScroll extends StatefulWidget {
#override
State createState() => new CustomScrollState();
}
class CustomScrollState extends State<CustomScroll> {
ScrollController scrollController;
double offset = 0.0;
static const double kEffectHeight = 100.0;
#override
Widget build(BuildContext context) {
return new Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget> [
new Container(
color: Colors.blue,
height: (kEffectHeight - offset * 0.5).clamp(0.0, kEffectHeight),
),
new Positioned(
child: new Container(
width: 200.0,
child: new ListView.builder(
itemCount: 100,
itemBuilder: buildListItem,
controller: scrollController,
),
),
),
],
);
}
Widget buildListItem(BuildContext context, int index) {
return new Container(
color: Colors.white,
child: new Text('Item $index')
);
}
void updateOffset() {
setState(() {
offset = scrollController.offset;
});
}
#override
void initState() {
super.initState();
scrollController = new ScrollController();
scrollController.addListener(updateOffset);
}
#override
void dispose() {
super.dispose();
scrollController.removeListener(updateOffset);
}
}
I think the SliverAppbar widget is what you are looking for.
Take a look at this article which shows you how to achieve your goal.
https://flutterdoc.com/animating-app-bars-in-flutter-cf034cd6c68b
Your should also take a look at https://medium.com/#diegoveloper/flutter-collapsing-toolbar-sliver-app-bar-14b858e87abe