How could I implement that effect in Flutter? - flutter

How Could I Implement this kind of effect?
I tried two SilverAppBar but it leads to a big blank strip between the two bars!
It's the same effect on the Facebook Mobile application on the home page.
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: CustomScrollView(
slivers: [
SliverAppBar(
title: Text('The App'),
),
SliverAppBar(
title: Row(
children: List<Widget>.generate(
6, (index) => Icon(Icons.access_alarm,color: Colors.grey,)),
mainAxisAlignment: MainAxisAlignment.spaceAround,
),
pinned: true,
backgroundColor: Colors.white,
),
SliverList(
delegate: SliverChildListDelegate(List<Widget>.generate(
100, (index) => Text(index.toString()))))
],
),
),
],
),
);
}
}

Related

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

TextFormField Cursor bubble and scrolling issue in flutter

When scrolling the page, Cursor bubble overlap the other widgets and Appbar. Can you help me?
This GIF file shows my issue
Widget
class Sample extends StatefulWidget {
Sample({Key? key}) : super(key: key);
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
extendBodyBehindAppBar: false,
extendBody: false,
appBar: AppBar(
title: Text('AppBar'),
backgroundColor: Colors.orange,
elevation: 0.0,
),
body: SafeArea(
child: Column(
children: [
ListView(
addAutomaticKeepAlives: true,
shrinkWrap: true,
children: [
Container(
color: Colors.yellow,
height: 70,
width: MediaQuery.of(context).size.width,
child: Center(
child: Text(
'This part want not be scrolled',
style: TextStyle(color: Colors.red),
),
),
)
],
),
Expanded(
child: Scrollbar(
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: [
Table(
children: [
TableRow(children: [
Column(
children: [Text('Name')],
),
Column(
children: [
TextFormField(decoration: InputDecoration())
],
)
]),
TableRow(
children: [
Column(
children: [Text('Name')],
),
Column(
children: [
TextFormField(decoration: InputDecoration())
],
)
],
),
TableRow(
children: [
Column(
children: [Text('Name')],
),
Column(
children: [
TextFormField(decoration: InputDecoration())
],
)
],
),
TableRow(
children: [
Column(
children: [Text('Name')],
),
Column(
children: [
TextFormField(decoration: InputDecoration())
],
)
],
),
],
),
],
),
),
),
],
),
),
);
}
}
Table widget is made for non-scrollable GridView. It renders full Table widget tree once and keep its children alive. You can think it is similar like SingleChildScrollView. Here in your Table children generate only once and don't call dispose even though it is not visible on screen, and it is the nature of Table Widget. To test this, you create a statefullWidget and pass it to column children.
For more
Table (Flutter Widget of the Week)
Table-class
Solution
you can simply use ListView.Builder

Add image banner above app bar title in Flutter

I need to add an image at the top of the app bar in Flutter. Below the image will be the title and action icons. How do I achieve this?
You can use CustomScrollView then you can find SliverAppBar>bottom
Updated Result
Updated Widget
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 300,
floating: true,
flexibleSpace: FlexibleSpaceBar(
background: Container(
color: Colors.cyanAccent,
child: Image.asset(
"assets/ocean.jpg",
fit: BoxFit.cover,
)),
),
),
SliverToBoxAdapter(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(),
Text("Title"),
Row(
children: [
IconButton(
onPressed: () {
print("tapped");
},
icon: Icon(Icons.ac_unit),
)
],
)
],
),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
height: 722,
color: Colors.pinkAccent,
),
],
),
),
],
),
),
);
}
}

Use a Row as title in FlexibleSpaceBar [Flutter]

I would like to use a Row widget instead of a Text widget as the title for the FlexibleSpaceBar.
Unfortunately Flutter returns an error.
Here is my code:
CustomScrollView(
slivers: [
const SliverAppBar(
pinned: true,
floating: false,
backgroundColor: Color(0xFF172A3A),
snap: false,
expandedHeight: 200,
centerTitle: true,
flexibleSpace: FlexibleSpaceBar(
background: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Demo'),
]
),
centerTitle: true,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
I know that the title should be a Text widget but I actually need a Row.
You can copy paste run full code below
Step 1 : Remove const keyword in front of SliverAppBar
Step 2 : Use Row like this
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('title'),
Text('test'),
]),
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
floating: false,
backgroundColor: Color(0xFF172A3A),
snap: false,
expandedHeight: 200,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('title'),
Text('test'),
]),
flexibleSpace: FlexibleSpaceBar(
background: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Demo'),
]),
centerTitle: true,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
);
}
}

TabView inside CustomScrollView

Wrapping TabBarView with SliverFillRemaining (fill remaining empty space like Expanded) gives the following error output.
flutter: A RenderPositionedBox expected a child of type RenderBox but received a child of type
flutter: RenderSliverList.
TabController tabContoller;
#override
void initState() {
tabContoller = new TabController(
vsync: this,
length: 3,
);
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton:floatActionBtn(...),
bottomNavigationBar: bottomNavigationBar(...),
appBar: apBar(),
body: Stack(
children: <Widget>[
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
expandedHeight: 195.0,
flexibleSpace: FlexibleSpaceBar(
background: new Stack(
children: <Widget>[
...
]),
),
),
new SliverFillRemaining(
child: TabBarView(
controller: tabContoller,
children: <Widget>[
Tab(...),
Tab(...),
Tab(...)
],
),
),
],
),
],
)
Don't forget the DefaultTabController, this code is working fine:
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Container(
child: CustomScrollView(slivers: <Widget>[
SliverAppBar(),
new SliverFillRemaining(
child: TabBarView(
children: <Widget>[
Text("Tab 1"),
Text("Tab 2"),
Text("Tab 3"),
],
),
),
])),
);
}
You can use NestedScrollView like this
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
forceElevated: innerBoxIsScrolled,
automaticallyImplyLeading: false,
expandedHeight: 195.0,
flexibleSpace: FlexibleSpaceBar(
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(_, int index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
labelColor: AppColors.black,
unselectedLabelColor: AppColors.gray,
indicatorColor: AppColors.primaryColor,
indicatorWeight: 4,
indicatorPadding: EdgeInsets.symmetric(horizontal: 16),
labelPadding: EdgeInsets.zero,
tabs: [
Text("FIRST"),
Text("SECOND"),
Text("THIRD"),
],
)
],
);
},
childCount: 1,
),
),
];
},
body: TabBarView(
children: <Widget>[
Text("FIRST TAB"),
Text("SECOND TAB"),
Text("THIRD TAB"),
],
),
),
),
);
}