How can I disable scrolling when there is a widget with CupertinoNavBar and large title? For instance I have:
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){
return <Widget>[
CupertinoSliverNavigationBar(
largeTitle: Text('Settings'),
)
];
},
body: Center(child: Text('Home Page'),),
),
);
}
if I add physics: NeverScrollableScrollPhysics(), it doesn't work
Related
I want to wrap this widget inside a Scaffold in order to include some title text for my app. However it returns an empty screen when I wrap it with Scaffold. Why is that?
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: [
SizedBox(
height: Dimensions.pageView,
child: PageView.builder(
//depende de dos parámetros el builder. Position cogerá desde el index 0 al itemcount.
itemCount: items.length,
controller: pageController,
itemBuilder: (context, position) {
return _buildPageItem(position);
}),
),
... MORE BASIC CHILDRENS
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Title'),
),
body: Column(
.....
),
);
}
Note that you don't need SafeArea when using AppBar
When I use a ScrollController in a ListView, it blocks the CupertinoSliverNavigationBar largeTitle from transitioning to a smallTitle. However, if I remove the scrollController, the problem goes away. I think it might be a bug in the Cupertino Library
This code demonstrates the issue:
ScrollController scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
CupertinoSliverNavigationBar(
largeTitle: Text('Large Title'),
),
];
},
body: ListView.builder(
controller: scrollController,
itemCount: 50,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
child: Center(child: Text('Entry ${index}')),
);
}),
),
);
}
Now if I remove the scrollController, the problem is gone:
ScrollController scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
CupertinoSliverNavigationBar(
largeTitle: Text('Large Title'),
),
];
},
body: ListView.builder(
//controller: scrollController,
itemCount: 50,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
child: Center(child: Text('Entry ${index}')),
);
}),
),
);
}
This is an expected behavior since NestedScrollView is meant to manage the scroll positions of all other child scrolling views:
NestedScrollView class
A scrolling view inside of which can be nested other scrolling views,
with their scroll positions being intrinsically linked.
So, you cannot control the positions of its children views with an individual ScrollController. However, you can provide one for NestedScrollView to manage all at once.
NotificationListener
There is still a way to listen to the scroll events of the inner scroll view besides using ScrollController. You can have your ListView inside of NotificationListener and check the scroll info that it provides.
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
CupertinoSliverNavigationBar(
largeTitle: Text('Large Title'),
),
];
},
body: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
print('Reached the bottom');
}
return;
},
child: ListView.builder(
itemCount: 50,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
child: Center(child: Material(child: Text('Entry $index'))),
);
},
),
),
),
);
}
I want to know how to drag the DraggableScrollableSheet widget to go up or down programmatically. For example, based on a timer class.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('DraggableScrollableSheet'),
),
body: SizedBox.expand(
child: DraggableScrollableSheet(
builder: (BuildContext context, ScrollController scrollController) {
return Container(
color: Colors.blue[100],
child: ListView.builder(
controller: scrollController,
itemCount: 25,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
);
},
),
),
);
}
}
I have the following code in one of my screens. I need to add a line of text above the ListView widget. I've tried adding a Column widget above and below the Container that holds the ListView, but I can't seem to get it right.
Can anyone tell me how I do this?
Cheers,
Paul
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: rmoAppBar(subText: 'My Jobs'),
drawer: RmoMenu(),
body: isLoading
? Center(child: CircularProgressIndicator())
: Container(
child: ListView.builder(
itemCount: jobs.sjRows.length,
itemBuilder: (BuildContext context, int index) {
return jobCard(jobs.sjRows, index);
},
),
),
),
onWillPop: () => logoutAlert(context: context),
);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: rmoAppBar(subText: 'My Jobs'),
drawer: RmoMenu(),
body: isLoading
? Center(child: CircularProgressIndicator())
: Column(
children: [
Text('Your text goes here'),
Expanded(
child: ListView.builder(
itemCount: jobs.sjRows.length,
itemBuilder: (BuildContext context, int index) {
return jobCard(jobs.sjRows, index);
},
),
)
],
),
),
onWillPop: () => logoutAlert(context: context),
);
}
The layout works as desired, except this:
When I scroll on one page, the second page scrolls too. Not as much but enough to obscure the first item.
I could imagine it'd have something to do with the NestedScrollView but I don't know how to go on.
import 'package:flutter/material.dart';
main(){
runApp(new MaterialApp(
home: new MyHomePage(),
));
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new DefaultTabController(
length: 2,
child: new Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
title: const Text('Tabs and scrolling'),
forceElevated: innerBoxIsScrolled,
pinned: true,
floating: true,
bottom: new TabBar(
tabs: <Tab>[
new Tab(text: 'Page 1'),
new Tab(text: 'Page 2'),
],
),
),
];
},
body: new TabBarView(
children: <Widget>[
_list(),
_list(),
],
),
),
),
);
}
Widget _list(){
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: 250,
itemBuilder: (context, index){
return Container(
color: Colors.grey[200].withOpacity((index % 2).toDouble()),
child: ListTile(
title: Text(index.toString()),
),
);
}
);
}
}
To be able to keep the two ListViews to scroll without affecting each other they need to have defined controllers.
To have the ListViews maintain their scroll position between tab switching you need to have them in a Widget with AutomaticKeepAliveClientMixin.
Here's an example of what you can do instead of your _list method. Defined a Stateful Widget that returns your lists using both controllers and the AutomaticKeepAliveClientMixin:
class ItemList extends StatefulWidget {
#override
_ItemListState createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> with AutomaticKeepAliveClientMixin{
ScrollController _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
super.build(context);
return ListView.builder(
controller: _scrollController,
padding: EdgeInsets.zero,
itemCount: 250,
itemBuilder: (context, index){
return Container(
color: Colors.grey[200].withOpacity((index % 2).toDouble()),
child: ListTile(
title: Text(index.toString()),
),
);
}
);
}
#override
bool get wantKeepAlive => true;
}
You can just call it normally like any other widget inside your TabBarView:
TabBarView(
children: <Widget>[
ItemList(),
ItemList(),
],
),