Flutter implementing instagram profile page - flutter

in this below code i want to make a simple Instagram profile page, my problem is adding tabs and TabBarView in side Sliver which i get some error
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: InstagramProfile(),
);
}
}
class InstagramProfile extends StatefulWidget {
#override
_InstagramProfileState createState() => _InstagramProfileState();
}
class _InstagramProfileState extends State<InstagramProfile> with TickerProviderStateMixin {
TabController tab;
#override
void initState() {
super.initState();
tab = TabController(length: 4, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.grey[100],
title: Container(
color: Colors.grey[100],
height: 200,
width: MediaQuery.of(context).size.width,
child: Center(
child: Text(
"Head",
style: TextStyle(color: Colors.black),
),
),
),
),
SliverAppBar(
pinned: true,
backgroundColor: Colors.white,
title: Container(
color: Colors.white,
constraints: BoxConstraints.expand(height: 50),
child: TabBar(
controller: tab,
tabs: [
Tab(text: "Event"),
Tab(text: "History"),
Tab(text: "Page"),
Tab(text: "Group"),
]),
),
),
SliverPadding(
padding: const EdgeInsets.all(0),
sliver: SliverList(
delegate: SliverChildListDelegate([
TabBarView(
controller: tab,
children: [
Container(
child: Text("Articles Body"),
),
Container(
child: Text("User Body"),
),
Container(
child: Text("User Body"),
),
Container(
child: Text("User Body"),
),
],
),
]),
),
)
],
),
);
}
}
Error:
RenderBox was not laid out: _RenderScrollSemantics#6cf1a relayoutBoundary=up8 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1785 pos 12: 'hasSize'
'package:flutter/src/rendering/sliver_multi_box_adaptor.dart': Failed assertion: line 545 pos 12: 'child.hasSize': is not true.
The relevant error-causing widget was:
SliverList file:///C:/Users/mahdi/AndroidStudioProjects/social_calendar_pro/lib/tabbar.dart:57:21

import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: InstagramProfile(),
);
}
}
class InstagramProfile extends StatefulWidget {
#override
_InstagramProfileState createState() => _InstagramProfileState();
}
class _InstagramProfileState extends State<InstagramProfile>
with TickerProviderStateMixin {
TabController tab;
#override
void initState() {
super.initState();
tab = TabController(length: 4, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: CustomScrollView(
shrinkWrap: true,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.grey[100],
title: Container(
color: Colors.grey[100],
height: 200,
width: MediaQuery.of(context).size.width,
child: Center(
child: Text(
"Head",
style: TextStyle(color: Colors.black),
),
),
),
),
SliverAppBar(
pinned: true,
title: Container(
// color: Colors.black12,
constraints: BoxConstraints.expand(height: 50),
child: TabBar(controller: tab, tabs: [
Tab(text: "Event"),
Tab(text: "History"),
Tab(text: "Page"),
Tab(text: "Group"),
]),
),
),
SliverPadding(
padding: const EdgeInsets.all(10),
sliver: SliverList(
delegate: SliverChildListDelegate([
Container(
child: TabBarView(
controller: tab,
children: [
Container(
child: Text("Articles Body"),
),
Container(
child: Text("User Body"),
),
Container(
child: Text("User Body"),
),
Container(
child: Text("User Body"),
),
],
),
height: 600,
)
]),
),
)
],
),
);
}
}
I hope this will work for you

Related

How to constrain ListTile in widget hierarchy

How can I make a ListTile work inside this widget hierarchy:
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Radio(
value: 1,
groupValue: 1,
onChanged: (sel) {},
),
GestureDetector(
child: Row(
children: <Widget>[
//Text('Title'), -- This is OK, but ListTile fails
ListTile(
title: Text('Title'),
subtitle: Text('Subtitle'),
),
],
),
onTap: () {},
),
],
),
],
),
),
Please understand that I am not in full control of the widget hierarchy -- a framework is building the SingleChildScrollView, Radio, GestureDetector, etc. I am simply trying to supply a child widget for an option. (I replicated the hierarchy in a simplified form to debug and experiment.)
I'm getting an exception:
flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown during performLayout():
flutter: BoxConstraints forces an infinite width.
flutter: These invalid constraints were provided to RenderParagraph's layout() function by the following
flutter: function, which probably computed the invalid constraints in question:
flutter: _RenderListTile._layoutBox (package:flutter/src/material/list_tile.dart:1318:9)
flutter: The offending constraints were:
flutter: BoxConstraints(w=Infinity, 0.0<=h<=Infinity)
Normally when I encounter this kind of problem, I simply wrap the widget in an Expanded, but that is not working in this case.
Note that if I replace the ListTile with a simple Text widget, then it renders fine.
You can wrap the ListTile in a Container or a ConstrainedBox and set its maxWidth:
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width - 50,
),
child: ListTile(
title: Text('Title'),
subtitle: Text('Subtitle'),
trailing: Text('Trailing'),
),
),
You can copy paste run full code below
You can use IntrinsicWidth wrap Row and Expanded wrap ListTile and you can have more than one Expaned ListTile
GestureDetector(
child: IntrinsicWidth(
child: Row(
children: <Widget>[
//Text('Title'), -- This is OK, but ListTile fails
Expanded(
child: ListTile(
title: Text('Title'),
subtitle: Text('Subtitle'),
),
),
],
),
),
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Radio(
value: 1,
groupValue: 1,
onChanged: (sel) {},
),
GestureDetector(
child: IntrinsicWidth(
child: Row(
children: <Widget>[
//Text('Title'), -- This is OK, but ListTile fails
Expanded(
child: ListTile(
isThreeLine: true,
title: Text('Title'),
subtitle: Text('this is long text this is long text this is long text \n'* 10),
),
),
],
),
),
onTap: () {},
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
full code 2 relayout
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Radio(
value: 1,
groupValue: 1,
onChanged: (sel) {},
),
),
Expanded(
flex: 5,
child: GestureDetector(
child: LayoutBuilder(
builder:(context, constraints) {
print(constraints.maxWidth);
print(constraints.minWidth);
return ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constraints.maxWidth),
child: ListTile(
//isThreeLine: true,
title: Text('Title'),
subtitle: Text(
'Textlargeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: Color(0xFF212121),
fontWeight: FontWeight.bold,
),
),
),
);
}
),
onTap: () {},
),
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Layout issue: TabBarView with TextField Keyboard overflow

I'm trying to implement a specific design and as thhe title says I have encountered an issue in building my layout.
When I'm taping inside my TextField the keyboard open itself and create an overflow.
Screenshots
Code
deals_edition_page.dart
import 'package:flutter/material.dart';
import 'package:myuca/ui/manager/deals/edition_informations_tab.dart';
class DealsEditionPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _DealsEditionPageState();
}
class _DealsEditionPageState extends State<DealsEditionPage> {
MemoryImage _image;
Widget _buildAppbar() {
return AppBar(
elevation: 0,
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
title: Text(/* random String */),
actions: [
IconButton(
icon: Icon(Icons.check),
onPressed: () => Navigator.pop(context),
),
],
);
}
Widget _buildHeaderTabBar() {
return SliverAppBar(
automaticallyImplyLeading: false,
expandedHeight: 224,
floating: true,
elevation: 0,
flexibleSpace:
FlexibleSpaceBar(background: Image.memory(_image.bytes, fit: BoxFit.cover)),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: DefaultTabController(
length: 2,
child: CustomScrollView(
slivers: <Widget>[
_buildHeaderTabBar(),
SliverToBoxAdapter(
child: TabBar(
indicatorWeight: 2,
tabs: [
Tab(text: 'Informations'),
Tab(text: 'Informations'),
],
),
),
SliverFillRemaining(
child: Column(
children: [
Expanded(
child: TabBarView(
children: [
EditionInformationsTab(),
Center(child: Text("Tab 2")),
],
),
),
],
),
),
],
),
),
);
}
}
edition_informations_tab.dart
import 'package:flutter/material.dart';
import 'package:myuca/extensions/extensions.dart' show WidgetModifier;
class EditionInformationsTab extends StatefulWidget {
#override
State<StatefulWidget> createState() => _EditionInformationsTabState();
}
class _EditionInformationsTabState extends State<EditionInformationsTab> {
final _list1 = <String>['Culture', 'Test'];
final _list2 = <String>['Etablissement', 'Test 1', 'Test 2'];
List<DropdownMenuItem<String>> _menuItemsGenerator(List<String> values) =>
values
.map<DropdownMenuItem<String>>((e) => DropdownMenuItem<String>(
value: e,
child: Text(e),
))
.toList();
#override
Widget build(BuildContext context) {
return Form(
child: Column(
children: [
DropdownButtonFormField<String>(
value: _list1.first,
decoration: InputDecoration(
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(4))),
items: _menuItemsGenerator(_list1),
onChanged: (_) {},
).padding(EdgeInsets.only(bottom: 24)),
DropdownButtonFormField(
value: _list2.first,
decoration: InputDecoration(
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(4))),
items: _menuItemsGenerator(_list2),
onChanged: (_) {},
).padding(EdgeInsets.only(bottom: 24)),
TextFormField(
maxLength: 30,
decoration: InputDecoration(
labelText: "Nom de l'offre",
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(4))),
),
],
),
).padding(EdgeInsets.only(
top: 16, left: 16, right: 16));
}
}
Any idea on how I could avoid this overflow when the keyboard is displayed ?
You can copy paste run full code below
You can in _EditionInformationsTabState wrap with SingleChildScrollView
class _EditionInformationsTabState extends State<EditionInformationsTab> {
...
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
working demo
full code
import 'package:flutter/material.dart';
class DealsEditionPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _DealsEditionPageState();
}
class _DealsEditionPageState extends State<DealsEditionPage> {
MemoryImage _image;
Widget _buildAppbar() {
return AppBar(
elevation: 0,
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
title: Text("demo"),
actions: [
IconButton(
icon: Icon(Icons.check),
onPressed: () => Navigator.pop(context),
),
],
);
}
Widget _buildHeaderTabBar() {
return SliverAppBar(
automaticallyImplyLeading: false,
expandedHeight: 224,
floating: true,
elevation: 0,
flexibleSpace: FlexibleSpaceBar(
background: Image.network("https://picsum.photos/250?image=9",
fit: BoxFit.cover)),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: DefaultTabController(
length: 2,
child: CustomScrollView(
slivers: <Widget>[
_buildHeaderTabBar(),
SliverToBoxAdapter(
child: TabBar(
indicatorWeight: 2,
tabs: [
Tab(text: 'Informations'),
Tab(text: 'Informations'),
],
),
),
SliverFillRemaining(
child: Column(
children: [
Expanded(
child: TabBarView(
children: [
EditionInformationsTab(),
Center(child: Text("Tab 2")),
],
),
),
],
),
),
],
),
),
);
}
}
class EditionInformationsTab extends StatefulWidget {
#override
State<StatefulWidget> createState() => _EditionInformationsTabState();
}
class _EditionInformationsTabState extends State<EditionInformationsTab> {
final _list1 = <String>['Culture', 'Test'];
final _list2 = <String>['Etablissement', 'Test 1', 'Test 2'];
List<DropdownMenuItem<String>> _menuItemsGenerator(List<String> values) =>
values
.map<DropdownMenuItem<String>>((e) => DropdownMenuItem<String>(
value: e,
child: Text(e),
))
.toList();
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(top: 16, left: 16, right: 16),
child: Form(
child: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 24),
child: DropdownButtonFormField<String>(
value: _list1.first,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4))),
items: _menuItemsGenerator(_list1),
onChanged: (_) {},
),
),
Padding(
padding: EdgeInsets.only(bottom: 24),
child: DropdownButtonFormField(
value: _list2.first,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4))),
items: _menuItemsGenerator(_list2),
onChanged: (_) {},
),
),
TextFormField(
maxLength: 30,
decoration: InputDecoration(
labelText: "Nom de l'offre",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4))),
),
],
),
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: DealsEditionPage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

How can I show/hide a floatingActionButton in one tab to another?

I was wondering how can I show/hide a FloatingActionButton in a TabBarView.
So for example in my first tab, I wanna a FloatingActionButton, but in the second tab I wanna hide it.
So in my case my code is:
class Notifications extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
floatingActionButton: FloatingActionButton(
backgroundColor: kPink,
child: Icon(Icons.add),
onPressed: () => print(Localizations.localeOf(context)),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: kBigSeparation),
child: DefaultTabController(
length: 2,
initialIndex: 0,
child: Padding(
padding: kPaddingTabBar,
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(kTinySeparation),
decoration: BoxDecoration(
color: kLightGrey,
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
child: TabBar(
tabs: <Tab>[
Tab(
text: AppLocalizations.of(context)
.translate('messages'),
),
Tab(
text: AppLocalizations.of(context)
.translate('notifications'),
)
],
unselectedLabelColor: Colors.black54,
labelColor: Colors.black,
unselectedLabelStyle: kBoldText,
labelStyle: kBoldText,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
color: Colors.white,
),
),
),
const SizedBox(height: kMediumSeparation),
Expanded(
child: TabBarView(children: [
MessagesTab(),
NotificationsTab(),
]),
),
],
),
),
),
),
),
);
}
}
You can copy paste run full code below
Step 1 : You need to use TabController
Step 2 : Show/hide floatingActionButton based on tabIndex
code snippet
TabBar(
controller: _tabController,
tabs: <Tab>[
...
floatingActionButton: _tabController.index == 0
? FloatingActionButton(
backgroundColor: Colors.pink,
child: Icon(Icons.add),
onPressed: () => print(Localizations.localeOf(context)),
)
: Container(),
working demo
full code
import 'package:flutter/material.dart';
class Notifications extends StatefulWidget {
#override
_NotificationsState createState() => _NotificationsState();
}
class _NotificationsState extends State<Notifications>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this, initialIndex: 0);
_tabController.addListener(_switchTabIndex);
}
void _switchTabIndex() {
print(_tabController.index);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
floatingActionButton: _tabController.index == 0
? FloatingActionButton(
backgroundColor: Colors.pink,
child: Icon(Icons.add),
onPressed: () => print(Localizations.localeOf(context)),
)
: Container(),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 2.0),
child: Padding(
padding: EdgeInsets.all(1.0),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
child: TabBar(
controller: _tabController,
tabs: <Tab>[
Tab(
text: 'messages',
),
Tab(
text: 'notifications',
)
],
unselectedLabelColor: Colors.black54,
labelColor: Colors.black,
//unselectedLabelStyle: kBoldText,
//labelStyle: kBoldText,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
color: Colors.white,
),
),
),
const SizedBox(height: 10),
Expanded(
child: TabBarView(controller: _tabController, children: [
MessagesTab(),
NotificationsTab(),
]),
),
],
),
),
),
),
);
}
}
class MessagesTab extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("MessagesTab");
}
}
class NotificationsTab extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("NotificationsTab");
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Notifications(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

SolidBottomSheet Controller Example Flutter

SolidBottomSheetController() is not working in my code, I am not able to listen_events of height or anything, hopefully, and I am sure my code is correct.
Can Anyone Please Give Example of SolidBottomSheet() working, with Controller, how you are implementing and listening to the events
You can copy paste run full code below , it's from official example and add listen event
You can use _controller.heightStream.listen
code snippet
SolidController _controller = SolidController();
#override
void initState() {
_controller.heightStream.listen((event) {
print(event);
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:solid_bottom_sheet/solid_bottom_sheet.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SolidController _controller = SolidController();
#override
void initState() {
_controller.heightStream.listen((event) {
print(event);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Solid bottom sheet example"),
),
body: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Image.asset("assets/cardImg.png"),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Flutter rules?",
style: Theme.of(context).textTheme.title,
),
),
],
),
ButtonTheme.bar(
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('NOPE'),
onPressed: () {
/* ... */
},
),
FlatButton(
child: const Text('YEAH'),
onPressed: () {
/* ... */
},
),
],
),
),
],
),
);
},
),
bottomSheet: SolidBottomSheet(
controller: _controller,
draggableBody: true,
headerBar: Container(
color: Theme.of(context).primaryColor,
height: 50,
child: Center(
child: Text("Swipe me!"),
),
),
body: Container(
color: Colors.white,
height: 30,
child: Center(
child: Text(
"Hello! I'm a bottom sheet ",
style: Theme.of(context).textTheme.display1,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.stars),
onPressed: () {
_controller.isOpened ? _controller.hide() : _controller.show();
}),
);
}
}

Hide the TabBar like a SliverAppBar

So there are many examples on the web where you can use a SliverAppBar that hides on scroll, and the TabBar below is still showing. I can't find anything that does it the other way around: When I scroll up I want to hide only the TabBar, keeping the AppBar persistent showing at all times. Does anyone know how to achieve this?
Here is a example with AppBar hiding (This is not what I want, just helps understand better what I want).
UPDATE
This is what I tried so far, and I thought it works, but the problem is I can't get the AppBar in the Positioned field to have the correct height (e.g. iPhone X its height is way bigger and overlaps with the tab bar).
// this sliver app bar is only use to hide/show the tabBar, the AppBar
// is invisible at all times. The to the user visible AppBar is below
return Scaffold(
body: Stack(
children: <Widget>[
NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
floating: true,
snap: true,
pinned: false,
bottom: TabBar(
tabs: [
Tab(
child: Text(
"1",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"2",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"3",
textAlign: TextAlign.center,
),
),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
children: [
MyScreen1(),
MyScreen2(),
MyScreen3(),
],
controller: _tabController,
physics: new NeverScrollableScrollPhysics(),
),
),
// Here is the AppBar the user actually sees. The SliverAppBar
// above will slide the TabBar underneath this one. However,
// I can´t figure out how to give it the correct height.
Container(
child: Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(
iconTheme: IconThemeData(
color: Colors.red, //change your color here
),
automaticallyImplyLeading: true,
elevation: 0,
title: Text("My Title"),
centerTitle: true,
),
),
),
],
),
);
Here is How you can do that, the idea is to use a postframecallback with the help of a GlobalKey to precalculate the appBar height and add an exapandedHeight like below,
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
TabController _tabController;
GlobalKey _appBarKey;
double _appBarHight;
#override
void initState() {
_appBarKey = GlobalKey();
_tabController = TabController(length: 3, vsync: this);
SchedulerBinding.instance.addPostFrameCallback(_calculateAppBarHeight);
super.initState();
}
_calculateAppBarHeight(_){
final RenderBox renderBoxRed = _appBarKey.currentContext.findRenderObject();
setState(() {
_appBarHight = renderBoxRed.size.height;
});
print("AppbarHieght = $_appBarHight");
}
#override
Widget build(BuildContext context) {
// this sliver app bar is only use to hide/show the tabBar, the AppBar
// is invisible at all times. The to the user visible AppBar is below
return Scaffold(
body: Stack(
children: <Widget>[
NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
floating: true,
expandedHeight: _appBarHight,
snap: true,
pinned: false,
bottom: TabBar(
tabs: [
Tab(
child: Text(
"1",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"2",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"3",
textAlign: TextAlign.center,
),
),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
children: [
MyScreen1(),
MyScreen2(),
MyScreen3(),
],
controller: _tabController,
physics: new NeverScrollableScrollPhysics(),
),
),
// Here is the AppBar the user actually sees. The SliverAppBar
// above will slide the TabBar underneath this one. However,
// I can¥t figure out how to give it the correct height.
Container(
key: _appBarKey,
child: Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: AppBar(
backgroundColor: Colors.red,
iconTheme: IconThemeData(
color: Colors.red, //change your color here
),
automaticallyImplyLeading: true,
elevation: 0,
title: Text("My Title"),
centerTitle: true,
),
),
),
],
),
);
}
}
class MyScreen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("My Screen 1"),
),
);
}
}
class MyScreen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("My Screen 2"),
),
);
}
}
class MyScreen3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("My Screen 3"),
),
);
}
}
Edit:
After more investigation I found a solution without keys or MediaQuery "stuff" by using just SafeArea Widget . please check the following Complete code :
import 'package:flutter/material.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 3, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
// this sliver app bar is only use to hide/show the tabBar, the AppBar
// is invisible at all times. The to the user visible AppBar is below
return Scaffold(
body: Stack(
children: <Widget>[
NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
primary: true,
floating: true,
backgroundColor: Colors.blue,//.withOpacity(0.3),
snap: true,
pinned: false,
bottom: TabBar(
tabs: [
Tab(
child: Text(
"1",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"2",
textAlign: TextAlign.center,
),
),
Tab(
child: Text(
"3",
textAlign: TextAlign.center,
),
),
],
controller: _tabController,
),
),
];
},
body: TabBarView(
children: [
MyScreen1(),
MyScreen2(),
MyScreen3(),
],
controller: _tabController,
physics: new NeverScrollableScrollPhysics(),
),
),
// Here is the AppBar the user actually sees. The SliverAppBar
// above will slide the TabBar underneath this one.
// by using SafeArea it will.
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: Container(
child: SafeArea(
top: false,
child: AppBar(
backgroundColor: Colors.blue,
// iconTheme: IconThemeData(
// color: Colors.red, //change your color here
// ),
automaticallyImplyLeading: true,
elevation: 0,
title: Text("My Title",),
centerTitle: true,
),
),
),
),
],
),
);
}
}
class MyScreen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Center(
child: Text("My Screen 1"),
),
);
}
}
class MyScreen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("My Screen 2"),
),
);
}
}
class MyScreen3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text("My Screen 3"),
),
);
}
}
Screenshot (Android)
Screenshot (iPhone X)
Your were very close, I have just modified couple of lines. I did it without using GlobalKey and other stuff (postFrameCallback etc). It is very simple and straightforward approach.
All you need to do is replace FlutterLogo with your own widgets which are MyScreen1, MyScreen2 and MyScreen3.
Code
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
floating: true,
snap: true,
pinned: true,
bottom: PreferredSize(
preferredSize: Size(0, kToolbarHeight),
child: TabBar(
controller: _tabController,
tabs: [
Tab(child: Text("1")),
Tab(child: Text("2")),
Tab(child: Text("3")),
],
),
),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
FlutterLogo(size: 300, colors: Colors.blue), // use MyScreen1()
FlutterLogo(size: 300, colors: Colors.orange), // use MyScreen2()
FlutterLogo(size: 300, colors: Colors.red), // use MyScreen3()
],
physics: NeverScrollableScrollPhysics(),
),
),
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: MediaQuery.removePadding(
context: context,
removeBottom: true,
child: AppBar(
iconTheme: IconThemeData(color: Colors.red),
automaticallyImplyLeading: true,
elevation: 0,
title: Text("My Title"),
centerTitle: true,
),
),
),
],
),
);
}
}
I think its pretty easy using nested scaffolds. where you dont need to calculate any height. Just put the tabbar inside a SilverAppBar not below the SilverAppBar.
feel free to comment if that doesnt solve your problem.
Example:
return Scaffold(
appBar: AppBar(), //your appbar that doesnt need to hide
body: Scaffold(
appBar: SilverAppBar(
pinned: false,
floating: false,
flexibleSpace: new Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
new TabBar() //your tabbar that need to hide when scrolling
])
)
body: //your content goes here
)
);