Content over appbar in flutter? - flutter

I just want to make UI like content over the appbar, but I couldn't. Here is my code that I got from web. how to make the listview top of appbar and remove the card.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Sample2(),
),
);
}
class Sample2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
child: CustomScrollView(
slivers: [
SliverPersistentHeader(
delegate: MySliverAppBar(expandedHeight: 200),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, index) => ListTile(
title: Text("Index: $index"),
),
),
)
],
),
),
);
}
}
class MySliverAppBar extends SliverPersistentHeaderDelegate {
final double expandedHeight;
MySliverAppBar({#required this.expandedHeight});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
fit: BoxFit.cover,
),
Center(
child: Opacity(
opacity: shrinkOffset / expandedHeight,
child: Text(
"MySliverAppBar",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 23,
),
),
),
),
Positioned(
top: expandedHeight / 2 - shrinkOffset,
left: MediaQuery.of(context).size.width / 4,
child: Opacity(
opacity: (1 - shrinkOffset / expandedHeight),
child: Card(
elevation: 10,
child: SizedBox(
height: expandedHeight,
width: MediaQuery.of(context).size.width / 2,
child: FlutterLogo(),
),
),
),
),
],
);
}
#override
double get maxExtent => expandedHeight;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
Using stack is not the best way I think. May be I am wrong. but I think customScrollView and sliverappbar can do this. but I didn't get any tutorial or youtube videos. Most of the examples are listview below the appbar. I just want content over the appbar at first time, when it scrolls it should go below the appbar.

This will help you to hide appbar on scroll
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text('Sameple text'),
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
tabs: <Tab>[
Tab(text: 'ONE'),
Tab(text: 'TWO'),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Text(
"xyz",
style: TextStyle(fontSize: 60),
),
),
Text("xyz"),
],
),
),
);
}

Related

Use ListView horizontal like Tabview with flutter

I'm looking for a tutorial on using a horizontal ListView that behaves like a Tabview, ie displaying the link on the same screen.
Some links to propose?
thanks
Tab child can others widget too, use height on Tab and isScrollable:true on TabBar
class TabBarDemo extends StatelessWidget {
const TabBarDemo({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
isScrollable: true,
tabs: [
Tab(
height: 100, // height
icon: Card(
child: Container(
height: 100,
width: 100,
color: Colors.red,
),
)),
Tab(
icon: Icon(Icons.directions_transit),
),
Tab(
icon: Icon(Icons.directions_bike),
),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
}
}
more about tabs
And using PageView & ListView, it will be
class TabBarDemo extends StatelessWidget {
const TabBarDemo({super.key});
#override
Widget build(BuildContext context) {
final PageController controller = PageController();
return Scaffold(
body: Column(
children: [
SizedBox(
height: 100, //tab item height
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => GestureDetector(
onTap: () {
controller.animateToPage(index,
duration: Duration(milliseconds: 100),
curve: Curves.bounceIn);
},
child: Container(
height: 100,
width: 100,
color: Colors.red,
child: Card(
child: Text("tab $index"),
),
),
),
),
),
Expanded(
child: PageView.builder(
controller: controller,
itemBuilder: (context, index) {
return Center(
child: Text("$index"),
);
},
),
),
],
),
);
}
}
Also you can check CustomScrollView.
run this example and you will get the whole idea :
class ListTapPage extends StatefulWidget {
const ListTapPage({Key? key}) : super(key: key);
#override
State<ListTapPage> createState() => _ListTapPageState();
}
class _ListTapPageState extends State<ListTapPage> {
List<Widget> pages = [const Center(child: Text("one")),const Center(child: Text("two")),const Center(child: Text("three"),)];
List<String> names = ["one","two","three"];
List<Color> colors = [Colors.red,Colors.blue,Colors.yellow];
int _index = 0 ;
void changeIndex({required int num}){
setState((){
_index = num;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Positioned(top: 0,right: 0,left: 0,bottom: MediaQuery.of(context).size.height * 0.75,
child: SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: (context, index) {
return GestureDetector(
onTap:()=>changeIndex(num: index) ,
child: Container(alignment: Alignment.center,width: 200,height: 50,color: colors[index],child: Text(names[index])),
);
},
),
)
),
Positioned(
left: 0,
right: 0,
bottom: 0,
height: MediaQuery.of(context).size.height * 0.30,
child: pages[_index]
),
]
),
),
);
}
}
just return this widget in the material app ,see the result and look at the code , you will understand , it's a simple demo.

Flutter NestedScrollView with TabBarView scrolls way too much when the body content is less

Here is the scenario -
Need TabBarView as user can swipe to change the screen.
Want to load more items when user scrolls to the bottom of the screen.
The first code is the NestedScrollView with TabBarView which has two tabs containing listview with 4 items. Even though the body height is less than screen height the body scrolls. I understand the default height is set to view port height but if I want achieve point number 2, I cant since the scroll is way too much. Is there a way to wrap the body to the height of the content?
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested Scroll Demo with TabBarView',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: NestedScrollViewTest(),
);
}
}
class NestedScrollViewTest extends StatelessWidget {
const NestedScrollViewTest({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var _tabs = ["One", "Two"];
return Scaffold(
body: DefaultTabController(
length: _tabs.length, // This is the number of tabs.
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
forceElevated: true,
elevation: 2.0,
primary: true,
pinned: true,
stretch: true,
backgroundColor: Colors.white,
expandedHeight: 500,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
centerTitle: true,
background: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 4,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.red,
),
),
),
SizedBox(
height: 5,
),
Expanded(
flex: 5,
child: Container(
color: Colors.amber,
),
),
SizedBox(
height: 5,
)
],
),
),
),
),
];
},
body: TabBarView(
children: _tabs.map((String name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (BuildContext context) {
return CustomScrollView(
// shrinkWrap: true, // even with this it is not working.
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverFixedExtentList(
itemExtent: 48.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount: 4,
),
),
),
],
);
},
),
);
}).toList(),
),
),
),
);
}
}
In the second code, I am using a CustomScrollView instead. Here since there is no SliverTabBarView, I am using a SliverFillRemaining widget to wrap the TabBarView and place it in the CustomScrollView. Even here the body scrolls way too much since SliverFillRemaining default height is view port height. Without using the TabBarView the CustomScrollView wraps the body based on the height of the content but I need TabBarView.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested Scroll Demo with TabBarView',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CustomScrollViewTest(),
);
}
}
class CustomScrollViewTest extends StatelessWidget {
const CustomScrollViewTest({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var _tabs = ["One", "Two"];
return Scaffold(
body: DefaultTabController(
length: _tabs.length, // This is the number of tabs.
child: CustomScrollView(
slivers: [
SliverAppBar(
forceElevated: true,
elevation: 2.0,
primary: true,
pinned: true,
stretch: true,
backgroundColor: Colors.white,
expandedHeight: 500,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
centerTitle: true,
background: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 4,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.red,
),
),
),
SizedBox(
height: 5,
),
Expanded(
flex: 5,
child: Container(
color: Colors.amber,
),
),
SizedBox(
height: 5,
)
],
),
),
),
SliverFillRemaining(
// hasScrollBody: false,
child: TabBarView(
children: _tabs.map((String name) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
itemCount: 4,
);
}).toList(),
),
)
],
),
),
);
}
}
Steps I have tried,
If I change the property hasScrollBody: false in SliverFillRemaining, I get the error -
RenderViewport does not support returning intrinsic dimensions.
If I use SliverToBoxAdapter instead of SliverFillRemaining then I get this error since TabBarView height is dependent on the parent.
Horizontal viewport was given unbounded height.
Is there a way to wrap the content based on the body height keeping TabBarView in mind.
Edit: adding images -
Initial
start scroll
end scroll

Scaling content on drag down in Flutter

I want to achieve the following behavior in Flutter.
Do you know if there is a built-in widget in Flutter that provides that functionality out of the box?
try this CustomScrollView:
LayoutBuilder(
builder: (context, constraints) {
return CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
delegate: Delegate(),
),
SliverToBoxAdapter(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
border: Border.all(
width: 2.0,
color: Colors.deepPurple,
),
),
height: constraints.biggest.height,
child: Center(
child: Text('Drag me down (or up) in order to see (or hide) the red icon on the top',
textAlign: TextAlign.center,
textScaleFactor: 5.0,
),
),
),
),
],
);
}
),
the "delegate" class is as follows:
class Delegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// print(shrinkOffset);
return Opacity(
opacity: 1 - shrinkOffset / maxExtent,
child: FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
);
}
#override double get maxExtent => 300;
#override double get minExtent => 100;
#override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
now try to drag the text up and down
EDIT: and this is a little modification to be similar like your example:
LayoutBuilder(
builder: (context, constraints) {
return CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
pinned: true,
delegate: Delegate(),
),
SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.only(top: 75),
height: constraints.biggest.height,
child: Text('Drag me down (or up) in order to make the red icon bigger (or smaller)',
textAlign: TextAlign.center,
textScaleFactor: 5.0,
),
),
),
],
);
}
),
and the modified delegate class:
class Delegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// print(shrinkOffset);
return OverflowBox(
maxHeight: 400,
alignment: Alignment.topCenter,
child: Container(
height: 400 - shrinkOffset,
child: FittedBox(child: Icon(Icons.alarm, color: Colors.red,), fit: BoxFit.contain),
),
);
}
#override double get maxExtent => 300;
#override double get minExtent => 1;
#override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
SliverAppBar now provides this feature out of the box using the stretch property:
SliverAppBar(
stretch: true, // <- this
flexibleSpace: FlexibleSpaceBar(
background: Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg', fit: BoxFit.cover),
title: Text('Hello again, SliverAppBar'),
stretchModes: <StretchMode>[
StretchMode.zoomBackground, // <- and this
],
),
);
Ref:
DartPad Example
Youtube Video

Flutter CustomScrollView scrolls when there are not enough items

Having an issue with scrolling when using slivers and a sliver appbar. How can I prevent the scrollview from scrolling when no scroll is needed like in the video. And if there are enough items for scrolling it should scroll (That works perfectly)
I followed this medium post. And you can see that he has the same problem.
https://medium.com/#diegoveloper/flutter-collapsing-toolbar-sliver-app-bar-14b858e87abe
https://youtu.be/l1EwM9GAfxw
class HomeScreen extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverAppBar(
brightness: Brightness.dark,
backgroundColor: Colors.amber.withOpacity(0.5),
expandedHeight: 166,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 80,
width: 80,
child: Placeholder(),
),
Row(
children: [
Text(
'sdalkf',
),
],
),
],
),
),
),
),
SliverPersistentHeader(
delegate: SliverAppBarDelegate(
MediaQuery.of(context).padding.top,
Container(
color: Colors.amber.withOpacity(0.5),
child: SafeArea(
bottom: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('sdfaklladsjfkladslkf\nsadjflkasjklfs\nsdkjlfjlkadslfjk'),
],
),
),
),
),
pinned: true,
),
],
body: GridView.builder(
itemCount: 3,
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 0.68),
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(
color: Colors.black54,
),
),
),
),
);
}
}
class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
final double topSafeArea;
_SliverAppBarDelegate(this.topSafeArea, this.child);
#override
double get minExtent => 105 + topSafeArea;
#override
double get maxExtent => 105 + topSafeArea;
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
child: child,
);
}
#override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
Related to:
Flutter SliverAppBar with Tabs overlays content
The above link is not a sollution but a temp fix
Set physics behavior of ScrollView to AlwaysScrollableScrollPhysics
from documentation:
/// Scroll physics that always lets the user scroll.
///
/// This overrides the default behavior which is to disable scrolling
/// when there is no content to scroll. It does not override the
/// handling of overscrolling.

How to use scrollable widget to draw one UI with complex header?

I am the new learner on Flutter. Now I have to implement one UI (Link) for one project. I think I should use CustomerScrollView or NestedScrollView to finish it. I tried my best but I sucked at implementing the header SliverAppBar and ADBar. Could anyone help to give some suggestions for it?
I tried to use NestedScrollView, and put all items into SliverAppBar, I encountered one problem. If I used FlexibleSpaceBar, all items were all zoomed out of the phone when initializing, you could see nothing until you scrolled up over a distance. If I gave up FlexibleSpaceBar and use normal Widget, it seemed OK, but if I tried to scroll up and once Ad Bar overlapped the text item, Error "BOTTOM OVERFLOWED XX PIXELS" would be disappeared.
class _MyPageState extends State<MyPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appbar: AppBar(/*build my AppBar*/),
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
...,
expandedHeight: 180,
flexibleSpace: FlexibleSpaceBar( //if use, all texts are out of phone when initializing
//if not use, Error('BOTTOM OVERFLOWED xx PIXELS') while scrolling up
title: Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Text 1'),
Text('Text 2'),
Text('Text 3'),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Text 4'),
Text('Text 5'),
Text('Text 6'),
],
),
],
)
),
),
//implement AD Bar
SliverPersistentHeader(
delegate: _SliverPersistentHeaderDelegate(_buildADBar())
),
)
];
},
body: //build body
GridView.count(
crossAxisCount: 2,
children: ...
),
),
);
}
}
as an option
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('App Bar'),
centerTitle: true,
elevation: 0,
),
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 180,
floating: false,
elevation: 0,
backgroundColor: Colors.green,
flexibleSpace: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Text 1'),
Text('Text 2'),
Text('Text 3'),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Text 4'),
Text('Text 5'),
Text('Text 6'),
],
),
],
),
),
),
),
SliverAppBar(
pinned: true,
elevation: 0,
flexibleSpace: Container(
alignment: Alignment.center,
height: kToolbarHeight,
color: Colors.redAccent,
child: Text('AD Bar'),
),
),
SliverGrid.count(
crossAxisCount: 2,
children: List.generate(20, (index) {
return Card(
color: Colors.white,
);
}),
),
],
),
),
);
}
}
This is what you need to do:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Collapsing List Demo')),
body: CollapsingList(),
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate({
#required this.minHeight,
#required this.maxHeight,
#required this.child,
});
final double minHeight;
final double maxHeight;
final Widget child;
#override
double get minExtent => minHeight;
#override
double get maxExtent => math.max(maxHeight, minHeight);
#override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent)
{
return new SizedBox.expand(child: child);
}
#override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
class CollapsingList extends StatelessWidget {
SliverPersistentHeader makeHeader(String headerText) {
return SliverPersistentHeader(
pinned: true,
delegate: _SliverAppBarDelegate(
minHeight: 60.0,
maxHeight: 200.0,
child: Container(
color: Colors.lightBlue, child: Center(child:
Text(headerText))),
),
);
}
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
makeHeader('Header Section 1'),
SliverGrid.count(
crossAxisCount: 3,
children: [
Container(color: Colors.red, height: 150.0),
Container(color: Colors.purple, height: 150.0),
Container(color: Colors.green, height: 150.0),
Container(color: Colors.orange, height: 150.0),
Container(color: Colors.yellow, height: 150.0),
Container(color: Colors.pink, height: 150.0),
Container(color: Colors.cyan, height: 150.0),
Container(color: Colors.indigo, height: 150.0),
Container(color: Colors.blue, height: 150.0),
],
),
makeHeader('Header Section 2'),
SliverFixedExtentList(
itemExtent: 150.0,
delegate: SliverChildListDelegate(
[
Container(color: Colors.red),
Container(color: Colors.purple),
Container(color: Colors.green),
Container(color: Colors.orange),
Container(color: Colors.yellow),
],
),
),
makeHeader('Header Section 3'),
SliverGrid(
gridDelegate:
new SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
return new Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: new Text('grid item $index'),
);
},
childCount: 20,
),
),
makeHeader('Header Section 4'),
// Yes, this could also be a SliverFixedExtentList. Writing
// this way just for an example of SliverList construction.
SliverList(
delegate: SliverChildListDelegate(
[
Container(color: Colors.pink, height: 150.0),
Container(color: Colors.cyan, height: 150.0),
Container(color: Colors.indigo, height: 150.0),
Container(color: Colors.blue, height: 150.0),
],
),
),
],
);
}
}
I'll definitely recommend that you read this Slivers Demystified
Hope this helps.