Related
In my application I am listing in an appBar several Containers that have the names of product categories, these categories are being listed in the body with their respective products.
The ListView that is in the appBar has the same indexes of the ListView of the body, so the idea was to press the index 'x' in the appBar and the user would be redirected to the index 'x' in the body.
I tried many solutions, one of then was the package https://pub.dev/packages/scrollable_positioned_list, but it did not works because when calling the function to scroll my list just disappears.
Here's de code:
return Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: Size.fromHeight(120),
child: Column(
children: [
AppBar(...),
Expanded(
child: Container(
color: AppColors.primary,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.listaProdutos.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(...),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(...),
child: Padding(...),
child: Center(
child: Text(
widget.listaProdutos[index].dsGrupo,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
onTap: () {
SHOULD SCROLL TO INDEX
},
),
);
},
)
),
),
],
),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.listaProdutos.length,
itemBuilder: (context, indexGrupo) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(...),
ListView.builder(..),
],
);
},
),
),
],
),
),
);
You can use PageView with scrollDirection: Axis.vertical,
class TFW extends StatefulWidget {
const TFW({super.key});
#override
State<TFW> createState() => _TFWState();
}
class _TFWState extends State<TFW> {
final PageController controller = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: PreferredSize(
preferredSize: Size.fromHeight(100),
child: Expanded(
child: ListView.builder(
itemCount: 100,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
controller.animateToPage(index,
duration: Duration(milliseconds: 400),
curve: Curves.easeIn);
},
child: SizedBox(width: 100, child: Text("$index")),
);
},
),
),
)),
body: PageView.builder(
controller: controller,
itemCount: 100,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Container(
color: index.isEven ? Colors.red : Colors.blue,
child: Text("$index"),
);
},
),
);
}
}
Fortunately I was able to resolve the problem. To help members who may have the same doubt I will register here the solution that worked for me. (sorry for the bad English)
Question: Why ScrollablePositionedList wasn't working? (as I mentioned iniatily)
Response: I was using the ScrollablePositionedList within a SingleChildScrollView, and for some reason when using the scrollTo or jumpTo function, the information that was visible simply disappeared. For that reason, I was trying to find a way to get success using a ListView (what came to nothing).
Solution: ... Trying to figure out why the ScrollablePositionedList wasn't working as it should ...
The initial structure was:
body: SingleChildScrollView(
child: Column(
children: [
Container(
child: ScrollablePositionedList.builder(
Changed for:
body: ScrollablePositionedList.builder(
The only reason for all this confusion is that ScrollablePositionedList's indexing functions for some reason don't work as they should if it's inside a SingleChildScrollView. So, take off SingleChildScrollView and all good.
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 want to use ListView with other widgets , but I can't. When I use container for Listview, I can't view any other widgets. How can I do it?
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(),
RaisedButton(
child: Text('Text'),
onPressed:(){})
])));
You shouldn't nest scroll views at all if you are trying to show some widgets based on a list, dart lets you use for inside any collection also you can use List.generate, or list.map with the spread operator
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
for(final item in list) widget,
RaisedButton(child: Text('Text'), onPressed: () {})
],
),
),
);
or
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
...list.map((item)=> widget).toList(),
RaisedButton(child: Text('Text'), onPressed: () {})
],
),
),
);
or
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
...List.generate(list.length, (index)=> widget).toList(),
RaisedButton(child: Text('Text'), onPressed: () {})
],
),
),
);
This is because you are using ListView inside Column, both ListView and Column take the full screen available to them, as this way we can only see ListView on the screen, to resolve this we have to shrink ListView to its exact size, for it shrinkwrap: true is used.
ListView.Builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
)
physics: NeverScrollableScrollPhysics(), is used here to stop ListView scrolling, you have added SingleChildScrollView() which scroll entire page
Add ShrinkWrap to The ListView
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView(
shrinkWrap: true,
children:[
Container(),
Container(),
]
),
RaisedButton(
child: Text('Text'),
onPressed:(){})
])));
for More Advanced Scrolling Challenges like More than One ListView in Column I Suggest you add a ScrollPhysics
u need use Expanded here and set data to ListView.builder
final items = List<String>.generate(10000, (i) => 'Item $i');
Column(children: [
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
),
AnyWidget(...)
])
You have to wrap your ListView widget with Expanded or if you want to wrap it with Container then you must have to give Container height or width
Try this Code...
Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: 4,
shrinkWrap: true,
itemBuilder: (context, index) {
return Text('Hello');
}
),
RaisedButton(
child: Text('Text'),
onPressed: () {}
)
]
)
)
);
In this example, the ListView and the other widget (a Container with yellow color) are both children of the Column widget. By doing this, you can ensure that the ListView and the other widgets can both be displayed on the screen.
Column(
children: <Widget>[
Container(
height: 200,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
color: Colors.green,
),
],
),
),
Container(
height: 200,
color: Colors.yellow,
),
],
)
I want to make scrollable page in flutter when we use Tabbar in flutter.
I tried this code but this is not working.
In this code my whole listview I cannot see. How to display whole listview items while using tabbar.
So, how can I solve this problem.
Widget _listofitem() {
return Container(
margin: EdgeInsets.only(top: 10.0),
padding: EdgeInsets.all(8.0),
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: categoryDetail.length,
itemBuilder: (BuildContext context, int index) {
return Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery.of(context).size.width,
height: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.cyanAccent),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: SizedBox(
height: 20,
width: 20,
// child: NetworkImage(categoryDetail[index]),
),
),
SizedBox(
height: 10.0,
),
Text(categoryDetail[index]['category_name'].toString()),
],
),
);
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
'Invitation Card Application',
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.cyan,
centerTitle: true,
bottom: TabBar(
tabs: myTabs,
controller: _tabController,
)
),
body: TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
return Center(
child: Stack(
children: <Widget>[
_listofitem(),
// _ofitem()
],
),
);
}).toList(),
));
}
I want to change page on individual tab click and also do scroll in that individual page. So I display whole my page. What is the solution for it.
In the ListView.builder() widget you have set the property,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
This is preventing the scroll on the list view. If you want to wrap the list view inside the container with the above properties, then wrap the container in the SingleChildScrollView widget.
SingleChildScrollView(
child: Container(),
);
By this, you can have the scroll effect and you will be able to see all the list view items
I've been investigating SliverAppBar, CustomScrollView, NestedScrollView, SliverPersistentHeader, and more. I cannot find a way to build something like the Instagram user profile screen's header where only the tab bar is pinned. The main body of the screen is a TabBarView and each pane has a scrollable list.
With SliverAppBar, it is easy to add the TabBar in the bottom parameter. But I want to have an extra widget of unknown/variable height above that TabBar. The extra widget should scroll out of the way when the page is scrolled and and then the TabBar is what is pinned at the top of the screen.
All I could manage was a fixed content before the tab bar and a fixed tab bar. I cannot get the header to scroll up and stick the TabBar at the top just just below the AppBar.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text("pabloaleko"),
),
body: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverToBoxAdapter(
child: SafeArea(
child: Text("an unknown\namount of content\n goes here in the header"),
),
),
SliverToBoxAdapter(
child: TabBar(
tabs: [
Tab(child: Text('Days', style: TextStyle(color: Colors.black))),
Tab(child: Text('Months', style: TextStyle(color: Colors.black))),
],
),
),
SliverFillRemaining(
child: TabBarView(
children: [
ListView(
children: <Widget>[
ListTile(title: Text('Sunday 1')),
ListTile(title: Text('Monday 2')),
ListTile(title: Text('Tuesday 3')),
ListTile(title: Text('Wednesday 4')),
ListTile(title: Text('Thursday 5')),
ListTile(title: Text('Friday 6')),
ListTile(title: Text('Saturday 7')),
ListTile(title: Text('Sunday 8')),
ListTile(title: Text('Monday 9')),
ListTile(title: Text('Tuesday 10')),
ListTile(title: Text('Wednesday 11')),
ListTile(title: Text('Thursday 12')),
ListTile(title: Text('Friday 13')),
ListTile(title: Text('Saturday 14')),
],
),
ListView(
children: <Widget>[
ListTile(title: Text('January')),
ListTile(title: Text('February')),
ListTile(title: Text('March')),
ListTile(title: Text('April')),
ListTile(title: Text('May')),
ListTile(title: Text('June')),
ListTile(title: Text('July')),
ListTile(title: Text('August')),
ListTile(title: Text('September')),
ListTile(title: Text('October')),
ListTile(title: Text('November')),
ListTile(title: Text('December')),
],
),
],
),
),
],
),
),
);
}
}
You can achieve this behaviour using NestedScrollView with Scaffold.
As we need the widgets between the AppBar and TabBar to be dynamically built and scrolled until TabBar reaches AppBar, use the appBar property of the Scaffold to build your AppBar and use headerSliverBuilder to build other widgets of unknown heights. Use the body property of NestedScrollView to build your tab views.
This way the elements of the headerSliverBuilder would scroll away till the body reaches the bottom of the AppBar.
Might be a little confusing to understand with mere words, here is an example for you.
Code:
// InstaProfilePage
class InstaProfilePage extends StatefulWidget {
#override
_InstaProfilePageState createState() => _InstaProfilePageState();
}
class _InstaProfilePageState extends State<InstaProfilePage> {
double get randHeight => Random().nextInt(100).toDouble();
List<Widget> _randomChildren;
// Children with random heights - You can build your widgets of unknown heights here
// I'm just passing the context in case if any widgets built here needs access to context based data like Theme or MediaQuery
List<Widget> _randomHeightWidgets(BuildContext context) {
_randomChildren ??= List.generate(3, (index) {
final height = randHeight.clamp(
50.0,
MediaQuery.of(context).size.width, // simply using MediaQuery to demonstrate usage of context
);
return Container(
color: Colors.primaries[index],
height: height,
child: Text('Random Height Child ${index + 1}'),
);
});
return _randomChildren;
}
#override
Widget build(BuildContext context) {
return Scaffold(
// Persistent AppBar that never scrolls
appBar: AppBar(
title: Text('AppBar'),
elevation: 0.0,
),
body: DefaultTabController(
length: 2,
child: NestedScrollView(
// allows you to build a list of elements that would be scrolled away till the body reached the top
headerSliverBuilder: (context, _) {
return [
SliverList(
delegate: SliverChildListDelegate(
_randomHeightWidgets(context),
),
),
];
},
// You tab view goes here
body: Column(
children: <Widget>[
TabBar(
tabs: [
Tab(text: 'A'),
Tab(text: 'B'),
],
),
Expanded(
child: TabBarView(
children: [
GridView.count(
padding: EdgeInsets.zero,
crossAxisCount: 3,
children: Colors.primaries.map((color) {
return Container(color: color, height: 150.0);
}).toList(),
),
ListView(
padding: EdgeInsets.zero,
children: Colors.primaries.map((color) {
return Container(color: color, height: 150.0);
}).toList(),
)
],
),
),
],
),
),
),
);
}
}
Output:
Hope this helps!
Another solution is that you could use a pinned SliverAppBar with FlexibleSpaceBar within DefaultTabController. Sample codes:
Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
floating: true,
pinned: true,
bottom: TabBar(
tabs: [
Tab(text: "Posts"),
Tab(text: "Likes"),
],
),
expandedHeight: 450,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Profile(), // This is where you build the profile part
),
),
];
},
body: TabBarView(
children: [
Container(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return Container(
height: 40,
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
Container(
child: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return Container(
height: 40,
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
),
),
),
Before scrolling:
After scrolling: