Customizing floating action button - flutter

I am using sliver list and I want to use both floating action button and sliding_up_panel from pub with the following behaviour: when I scroll down my list, the floating action button disappears; when I scroll up the fab appears. Moreover, the fab should disappear when I slide up (open) the menu.
As you can see above, the floating action button is all on the sliding element, but I want it to be between the sliding element and the scrolling item list.
Also in above picture, the problem is that the floating button is actually visible but I want to hide it with a nice animation when I slide up the sliding menu.
I hope my question is clear!!!

Edit please do it with scroll controller
full 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: SliverExample(
bodyWidgets: Text('Hello Body'),
backgroundWidget: Text('Hello Background'),
),
);
}
}
Widget listItem(Color color, String title) => Container(
height: 100.0,
color: color,
child: Center(
child: Text(
"$title",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
fontWeight: FontWeight.bold),
),
),
);
class SliverExample extends StatefulWidget {
final Widget backgroundWidget;
final Widget bodyWidgets;
SliverExample({
this.backgroundWidget,
this.bodyWidgets,
});
#override
_SliverExampleState createState() => _SliverExampleState();
}
class _SliverExampleState extends State<SliverExample> {
// I need something like this
// To determine if SliverAppBar is expanded or not.
ScrollController _scrollController;
bool isAppBarExpanded = false;
#override
void initState() {
super.initState();
_scrollController = ScrollController()
..addListener(() => setState(() {
print('Scroll view Listener is called offset ${_scrollController.offset}');
}));
}
bool get _changecolor {
return _scrollController.hasClients
&& _scrollController.offset > (200-kToolbarHeight);
}
bool get _hideFAB {
return _scrollController.hasClients
&& _scrollController.offset > (200-kToolbarHeight);
}
#override
Widget build(BuildContext context) {
// To change the item's color accordingly
// To be used in multiple places in code
//Color itemColor = isAppBarExpanded ? Colors.white : Colors.black;
// In my case PrimaryColor is white,
// and the background widget is dark
return Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
leading: BackButton(
color: _changecolor? Colors.white: Colors.black, // Here
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: _changecolor? Colors.white: Colors.black, // Here
),
onPressed: () {},
),
],
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
'title',
style: TextStyle(
fontSize: 18.0,
color: _changecolor? Colors.white: Colors.black, // Here
),
),
// Not affecting the question.
background: widget.backgroundWidget,
),
),
SliverList(
///Use SliverChildListDelegate and provide a list
///of widgets if the count is limited
///
///Lazy building of list
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
/// To convert this infinite list to a list with "n" no of items,
/// uncomment the following line:
/// if (index > n) return null;
return listItem(Colors.grey, "Sliver List item: $index");
},
/// Set childCount to limit no.of items
/// childCount: 100,
),
),
// Not affecting the question.
SliverToBoxAdapter(child: widget.bodyWidgets),
],
),
floatingActionButton: _hideFAB? Container() : FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add,),
),
);
}
}

Related

Flutter: Override 'PageView' scroll gesture with child's GestureDetector

I am using a combination of "BottomNavigationBar" and "PageView" for navigation in my app. The user can either swipe to the next page or use the navigation bar.
On one of my pages, I would like to use a gesture detector that handles pan gestures, both vertically and horizontally.
I can't find a way to override the PageView's gesture detection with the nested GestureDetector. This means only vertical pan gestures are handled, as the horizontal ones are occupied by the PageView.
How can I disable / override the PageViews gesture detection for only that page or only the widget, without completely disabling the PageViews scroll physics?
I have created a simplified version of my App to isolate the issue, and attached a video of the problem below.
Any help would be greatly appreciated!
Here is the code inside my main.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,
),
home: GestureIssueExample(),
);
}
}
class GestureIssueExample extends StatefulWidget {
GestureIssueExample({Key key}) : super(key: key);
#override
_GestureIssueExampleState createState() => _GestureIssueExampleState();
}
class _GestureIssueExampleState extends State<GestureIssueExample> {
int _navigationIndex;
double _xLocalValue;
double _yLocalValue;
PageController _pageController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
bottomNavigationBar: _buildBottomNavigationBar(),
backgroundColor: Colors.white,
body: PageView(
controller: _pageController,
onPageChanged: _onNavigationPageChanged,
children: [
//Just a placeholder to represent a page to the left of the "swipe cards" widget
_buildSamplePage("Home"),
//Center child of 'PageView', contains a GestureDetector that handles Pan Gestures
//Thanks to the page view however, only vertical pan gestures are detected, while both horizontal and vertical gestures
//need to be handled...
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Local X: ${_xLocalValue.toString()}\nLocal Y: ${_yLocalValue.toString()}"),
GestureDetector(
onPanStart: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
onPanUpdate: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
height: 100.0,
color: Colors.red,
alignment: Alignment.center,
child: Text("Slidable Surface",
style: TextStyle(color: Colors.white)),
),
),
],
),
),
//Just a placeholder to represent a page to the right of the "swipe cards" widget
_buildSamplePage("Settings"),
],
),
);
}
#override
void initState() {
super.initState();
this._navigationIndex = 0;
this._pageController = PageController(
initialPage: _navigationIndex,
);
}
Widget _buildSamplePage(String text) {
// This simply returns a container that fills the page,
// with a text widget in its center.
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.grey[900],
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
color: Colors.white, fontSize: 30.0, fontWeight: FontWeight.bold),
),
);
}
Widget _buildBottomNavigationBar() {
//Returns the bottom navigation bar for the scaffold
return BottomNavigationBar(
backgroundColor: Colors.grey[900],
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.white,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.check_box_outline_blank), label: "Cards"),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined), label: "Settings"),
],
currentIndex: _navigationIndex,
onTap: _onNavigationPageChanged,
);
}
void _onNavigationPageChanged(int newIndex) {
//Set the new navigation index for the nav bar
setState(() => this._navigationIndex = newIndex);
//Animate to the selected page
_pageController.animateToPage(
newIndex,
curve: Curves.easeInOut,
duration: Duration(microseconds: 100),
);
}
}
Can you try something like this:
Add this line to your PageView:
PageView(
...
physics: _navigationIndex == 1 ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
...
)
Note: the number 1 is because the page with the GestureDetector is on index 1.

Flutter Searchdelegate, i want to add background color and appbar color when i click the search

i can change my background color and app bar in home just fine, but when i click the search icon which uses search delegate it all back to white, how do i change the color? just to make it clear, so before the user clicked the search icon the background and app bar was black but when they clicked it it turned to white, how do i change it?
here is the search code :
import 'package:flutter/material.dart';
import 'package:movie_app_3/model/movie_response.dart';
import 'package:movie_app_3/screens/movie_detail_screen/movie_detail_screen.dart';
import '../model/movie.dart';
import '../repository/repository.dart';
class DataSearch extends SearchDelegate {
// void initState() {
// searchBloc..getSearch(query);
// }
final movieRepo = MovieRepository();
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () => query = '',
)
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow, progress: transitionAnimation),
onPressed: () => close(context, null),
);
}
#override
Widget buildResults(BuildContext context) {
return Container();
}
#override
Widget buildSuggestions(BuildContext context) {
if (query.isEmpty) return Container();
return FutureBuilder<MovieResponse>(
future: movieRepo.getSearch(query),
builder: (BuildContext context, AsyncSnapshot<MovieResponse> snapshot) {
if (snapshot.hasData) {
if (snapshot.data.error != null && snapshot.data.error.length > 0) {
return _buildErrorWidget(snapshot.data.error);
}
return _buildHomeWidget(snapshot.data);
} else if (snapshot.hasError) {
return _buildErrorWidget(snapshot.error);
} else {
return _buildLoadingWidget();
}
},
);
}
Widget _buildHomeWidget(MovieResponse data) {
List<Movie> movies = data.movies;
return ListView.builder(
itemCount: movies.length,
itemBuilder: (context, index) {
return ListTile(
leading: FadeInImage(
image: movies[index].poster == null
? AssetImage('assets/images/no-image.jpg')
: NetworkImage("https://image.tmdb.org/t/p/w200/" +
movies[index].poster),
placeholder: AssetImage('assets/images/no-image.jpg'),
width: 50.0,
fit: BoxFit.contain),
title: Text(
movies[index].title,
style: TextStyle(fontFamily: 'Poppins'),
),
subtitle: Text(
movies[index].overview,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontFamily: 'Raleway'),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MovieDetailScreen(movie: movies[index]),
),
);
},
);
},
);
}
Widget _buildLoadingWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 25.0,
width: 25.0,
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.black),
strokeWidth: 4.0,
),
)
],
));
}
Widget _buildErrorWidget(String error) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Error occured: $error"),
],
));
}
// #override
// Widget buildSuggestions(BuildContext context) {
// final suggestedList = (query.isEmpty) ?
// recentMovies :
// movies.where((movie) => movie.toLowerCase().contains(query.toLowerCase())).toList();
// return ListView.builder(
// itemCount: suggestedList.length,
// itemBuilder: (context, i) {
// return ListTile(
// leading: Icon(Icons.movie),
// title: Text(suggestedList[i]),
// onTap: () {},
// );
// },
// );
// }
}
here is the home code :
import 'package:flutter/material.dart';
import 'package:movie_app_3/widget/drawer.dart';
import 'package:movie_app_3/screens/home_screen/widget/home_screen1.dart';
import 'package:movie_app_3/screens/home_screen/widget/home_screen2.dart';
import 'package:movie_app_3/widget/search.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 2);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black,
Color(0xff112339),
Colors.black,
],
),
),
child: DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
elevation: 0,
title: Text(
'Moviez',
style: TextStyle(
fontSize: 24,
color: Colors.white,
fontFamily: 'Poppins',
),
),
backgroundColor: Colors.transparent,
centerTitle: true,
actions: [
Padding(
padding: EdgeInsets.only(right: 20),
child: IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(context: context, delegate: DataSearch());
},
),
),
],
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.tab,
indicatorWeight: 2.0,
tabs: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'Discover',
style: TextStyle(fontSize: 16, fontFamily: 'Raleway'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'Genres',
style: TextStyle(fontSize: 16, fontFamily: 'Raleway'),
),
),
],
),
),
drawer: MyDrawer(),
body: TabBarView(
controller: _tabController,
children: <Widget>[
FirstTab(),
SecondTab(),
],
),
),
),
);
}
}
For customizing the Search Delegate, you have to override a method called appBarTheme and then set your custom theme on that.
** NOTE: When you override appBarTheme of SearchDelegate you have to customize evrything related to SearchBar yourself. Just like the code below. **
Do this to change the AppBar Color:
#override
ThemeData appBarTheme(BuildContext context) {
return ThemeData(
appBarTheme: const AppBarTheme(
color: MyColors.mainColor, // affects AppBar's background color
hintColor: Colors.grey, // affects the initial 'Search' text
textTheme: const TextTheme(
headline6: TextStyle( // headline 6 affects the query text
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.bold)),
),
);
}
And for changing the background color of suggestions:
#override
Widget buildSuggestions(BuildContext context) {
return Container(
color: Colors.black,
...
);
}
Similarly do this for results:
#override
Widget buildResults(BuildContext context) {
return Container(
color: Colors.black,
...
);
}
Hope this helps.
Add this to your "DataSearch" class
class _SearchDelegate extends SearchDelegate {
#override
ThemeData appBarTheme(BuildContext context) {
return Theme.of(context).copyWith(
scaffoldBackgroundColor: Colors.green,
);
}
If you already set your MaterialApp theme you can simply use Theme.of(context).copywith to remain body theme. Then you can override appBarTheme to change desired color/styles.
#override
ThemeData appBarTheme(BuildContext context) {
return Theme.of(context).copyWith(
//scaffoldBackgroundColor: , to change scaffold color
appBarTheme: const AppBarTheme( //to change appbar
color: Colors.black,
//titleTextStyle: , to change title text
//toolbarTextStyle: , to change toolbar text style
),
);

Why flutter UI lag while soft keyboard appearing and disappearing?

When I click text fields on the main page (main.dart) which is the default dart given by the flutter. I can see a glitch when soft keyboard appears and there is no delay when soft keyboard disappears.I have attached a gif below for this case.
void main() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: primaryColor, //blue
statusBarIconBrightness: Brightness.dark,
));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.pink,
primaryColor: primaryColor,
primaryColorBrightness: Brightness.dark,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// setState(() {
// // This call to setState tells the Flutter framework that something has
// // changed in this State, which causes it to rerun the build method below
// // so that the display can reflect the updated values. If we changed
// // _counter without calling setState(), then the build method would not be
// // called again, and so nothing would appear to happen.
// _counter++;
// });
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PhoneAuth()));
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text('hell0000000'),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Container(
color: Colors.white,
child: ListView(
children: [
Column(
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
SizedBox(
height: 200,
),
Align(
alignment: Alignment.bottomCenter,
child: new Container(
child: TextField(
decoration: new InputDecoration(
hintText: 'Chat message',
),
),
),
),
],
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
main.dart glich
Also, When I click text fields on the other page (UserChatView.dart). I can see a glitch when soft keyboard appearing and disappearing. In this dart file, That glitch happening for both actions(Keyboard opening and closing). I have attached a gif below for this case.
class UserChatView extends StatelessWidget{
#override
Widget build(BuildContext context) {
return UserChatViewPage();
}
}
class UserChatViewPage extends StatefulWidget {
UserChatViewPage({Key key}) : super(key: key);
#override
_UserChatViewPageState createState() => _UserChatViewPageState();
}
class _UserChatViewPageState extends State<UserChatViewPage> {
final TextEditingController _textController = new TextEditingController();
#override
Widget build(BuildContext context) {
final focus = FocusNode();
return new Scaffold(
backgroundColor: Colors.red, // Scaffold background Color
appBar: new AppBar(
title: Row(
children: <Widget>[
// new Container(
// child: CircleAvatar(
// backgroundImage: AssetImage("assets/male_icon.png"),
// )
// ),
new SizedBox(
width: 5.00,
),
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget> [
new Container(
child: new Text("Alex Marko",
style: TextStyle(color: Colors.white,
fontFamily: 'Roboto_Bold',
letterSpacing: 1.00
),
),
),
new Container(
child: new Text("Online",
style: TextStyle(color: Colors.white,
fontFamily: 'Roboto_Medium',
letterSpacing: 1.00,
fontSize: 12.00,
),
),
),
],
),
],
),
centerTitle: false,
titleSpacing: 0.0,
backgroundColor: primaryColor,
elevation: 0.0,
bottomOpacity: 0.0,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.expand_more_rounded,
color: Colors.white,
),
onPressed: () {
// do something
},
),
],
),
body: Center(
child: new Container(
color: Colors.grey,
child: new Column(
children: <Widget>[
new Expanded(
child: _PageListView(),
),
new Container(
color: Colors.yellow,
padding: new EdgeInsets.all(10.0),
child: _buildTextComposer(),
),
],
),
),
),
);
}
Widget _buildTextComposer() {
return new Container(
color: Colors.yellow,//modified
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row(
children: <Widget>[
new Flexible(
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: new IconButton(
icon: new Icon(Icons.send),
onPressed: () => _handleSubmitted(_textController.text)),
),
],
),
);
}
Widget _PageListView(){
return new Container(
child: ListView.builder(
reverse: true,
itemCount: 20,
itemBuilder: (context, position) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(position.toString(), style: TextStyle(fontSize: 22.0),),
),
);
},
),
);
}
UserChatView.dart Glich
The reason of this so called Glitch is that the default behaviour of the flutter scaffold widget is to resize it's body when soft keyboard opens up or closes down.
While the flutter scaffold is notified of any of the above two events, it will start resizing the widgets under its body to match the new state. The speed of resizing may differ based on the comlplexity of build process of the widget on screen & processing speed of the device itself.
What you can do is add a flag called resizeToAvoidBottomInset in the scaffold widget in your app, like this:
Scaffold(
...
resizeToAvoidBottomInset: false,
)
What this does is, It notifies the scaffole to not resize the widget under its body on keyboard up or down states.
So, unless you want to explicitely resize the content of the screen on keyboard state, this is a solution to your glitch.
On the other hand, if you want to resize the content, you can choose to modularize/breakdown the widgets that you have on screen to smallest possible combination & make the layout simpler so that the glitch portion of the resizing is taken care of by the speed of rebuild process.
Your keyboard is producing that effect because of the ListView.builder. Add extra properties to your ListView like this
Widget _PageListView(){
return new Container(
child: ListView.builder(
addAutomaticKeepAlives: true, // Add this property
cacheExtent: double.infinity, // And this one
reverse: true,
itemCount: 20,
itemBuilder: (context, position) {
return Card( /* It's better to have here a separated StatefulWidget with AutomaticKeepAliveClientMixin */
// ...
);
},
),
);
}

Create reusable appBar in flutter

I am trying to create a reusable appBar in my flutter app. The right bar button of the appbar should be controlled from main UI where it is added. I am able to create appBar and use it but I am not able to change the text color of the button on the appbar. Following is my code for creating appBar:
class SocialAppBar extends StatefulWidget implements PreferredSizeWidget {
AppBarConfig appBarConfig;
bool isEnabled = false;
VoidCallback rightButtonClickCallback;
SocialAppBar({#required this.appBarConfig, #required this.rightButtonClickCallback});
#override
State<StatefulWidget> createState() {
return SocialAppBarState();
}
#override
Size get preferredSize => new Size.fromHeight(kToolbarHeight);
}
class SocialAppBarState extends State<SocialAppBar> {
#override
Widget build(BuildContext context) {
return getAppBarWithProfilePic();
}
Widget getAppBarWithProfilePic() {
return AppBar(
brightness: Brightness.light,
backgroundColor: Colors.white,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
key: const Key("backButton"),
onPressed: () {
Navigator.pop(context);
}),
iconTheme: const IconThemeData(
color: Colors.black54, //change your color here
),
titleSpacing: 0.0,
title: Row(
children: <Widget>[
buildAvatar(),
const SizedBox(
width: 15,
),
Text(widget.appBarConfig.fullName,
style: TextStyle(color: AppColor.appbarTitleSecondaryColor, fontWeight: FontWeight.w400))
],
),
actions: <Widget>[
Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(left: 20, right: 20),
child: InkWell(
child: AutoSizeText(
AppLocalizations.of(context).translate(GlobalString.labelNext),
style: TextStyle(color: widget.isEnabled ? AppColor.blue : AppColor.greyMediumDark, fontSize: 16),
textAlign: TextAlign.center,
),
onTap: widget.rightButtonClickCallback,
))
],
);
}
setNextButtonColor(){
setState(() {
});
}
}
I am using the above appBar like following in my screen:
void initState() {
super.initState();
appBar = SocialAppBar(appBarConfig: appBarConfig, rightButtonClickCallback: nextButtonClick);
//Next button on app bar should be enabled when following textController has any text
_textController.addListener((){
if (_textController.text.length > 0){
appBar.isEnabled = true;
}else {
appBar.isEnabled = false;
}
});
}
#override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: _createPostBloc,
builder: (context, state) {
if (state is CreatePostNextState) {}
return Scaffold(
appBar: this.appBar,
key: _scaffoldKey,
backgroundColor: Colors.white,
body: buildPageView(),
);
},
);
}
I am able to set the Next button enable/disable with the above code but unable to change the color from Gray to Blue.
Regards
reusable AppBar:
Widget appBar(String text, IconButton iconButton) {
return AppBar(
title: Text(
text,
style: AppTheme.screenTitleStyle(),
),
centerTitle: true,
leading: IconButton(icon: iconButton, onPressed: () {}),
backgroundColor: AppTheme.mainThemeColor(),
brightness: Brightness.dark,
);
}
Use this appBar in Activity like this :
appBar: appBar(
"Change Password",
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
),
Simple fix:
_textController.addListener((){
setState(() {
if (_textController.text.length > 0){
appBar.isEnabled = true;
}else {
appBar.isEnabled = false;
}
});
});

Flutter How to check if Sliver AppBar is expanded or collapsed?

I am using a SliverAppBar in Flutter, with a background widget.
The thing is When it's expanded, the title and icons (leading and actions) should be white in order to be seen correctly, and when it's collapsed, they should be changed to black.
Any ideas on how I can get a bool out of it? Or other ways of resolving this problem.
Thank you.
class SliverExample extends StatefulWidget {
final Widget backgroundWidget;
final Widget bodyWidgets;
SliverExample({
this.backgroundWidget,
this.body,
});
#override
_SliverExampleState createState() => _SliverExampleState();
}
class _SliverExampleState extends State<SliverExample> {
// I need something like this
// To determine if SliverAppBar is expanded or not.
bool isAppBarExpanded = false;
#override
Widget build(BuildContext context) {
// To change the item's color accordingly
// To be used in multiple places in code
Color itemColor = isAppBarExpanded ? Colors.white : Colors.black;
// In my case PrimaryColor is white,
// and the background widget is dark
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
leading: BackButton(
color: itemColor, // Here
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: itemColor, // Here
),
onPressed: () {},
),
],
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
'title',
style: TextStyle(
fontSize: 18.0,
color: itemColor, // Here
),
),
// Not affecting the question.
background: widget.backgroundWidget,
),
),
// Not affecting the question.
SliverToBoxAdapter(child: widget.body),
],
),
);
}
}
You can use LayoutBuilder to get sliver AppBar biggest height. When biggest.height = MediaQuery.of(context).padding.top + kToolbarHeight, it actually means sliver appbar is collapsed.
Here is a full example, in this example MediaQuery.of(context).padding.top + kToolbarHeight = 80.0:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var top = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// print('constraints=' + constraints.toString());
top = constraints.biggest.height;
return FlexibleSpaceBar(
centerTitle: true,
title: AnimatedOpacity(
duration: Duration(milliseconds: 300),
//opacity: top == MediaQuery.of(context).padding.top + kToolbarHeight ? 1.0 : 0.0,
opacity: 1.0,
child: Text(
top.toString(),
style: TextStyle(fontSize: 12.0),
)),
background: Image.network(
"https://images.unsplash.com/photo-1542601098-3adb3baeb1ec?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=5bb9a9747954cdd6eabe54e3688a407e&auto=format&fit=crop&w=500&q=60",
fit: BoxFit.cover,
));
})),
];
},body: ListView.builder(
itemCount: 100,
itemBuilder: (context,index){
return Text("List Item: " + index.toString());
},
),
));
}
}
Final result:
You need to use ScrollController to achieve the desired effect
try this 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: SliverExample(
bodyWidgets: Text(
'Hello Body gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg'),
backgroundWidget: Text('Hello Background'),
),
);
}
}
class SliverExample extends StatefulWidget {
final Widget backgroundWidget;
final Widget bodyWidgets;
SliverExample({
this.backgroundWidget,
this.bodyWidgets,
});
#override
_SliverExampleState createState() => _SliverExampleState();
}
class _SliverExampleState extends State<SliverExample> {
ScrollController _scrollController;
Color _theme ;
#override
void initState() {
super.initState();
_theme = Colors.black;
_scrollController = ScrollController()
..addListener(
() => _isAppBarExpanded ?
_theme != Colors.white ?
setState(
() {
_theme = Colors.white;
print(
'setState is called');
},
):{}
: _theme != Colors.black ?
setState((){
print(
'setState is called');
_theme = Colors.black;
}):{},
);
}
bool get _isAppBarExpanded {
return _scrollController.hasClients &&
_scrollController.offset > (200 - kToolbarHeight);
}
#override
Widget build(BuildContext context) {
// To change the item's color accordingly
// To be used in multiple places in code
//Color itemColor = isAppBarExpanded ? Colors.white : Colors.black;
// In my case PrimaryColor is white,
// and the background widget is dark
return Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
leading: BackButton(
color: _theme, // Here
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: _theme, // Here
),
onPressed: () {},
),
],
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
'title',
style: TextStyle(
fontSize: 18.0,
color: _theme, // Here
),
),
// Not affecting the question.
background: widget.backgroundWidget,
),
),
// Not affecting the question.
SliverToBoxAdapter(child: widget.bodyWidgets),
],
),
);
}
}
if you are not familiar with ? : notation you can use the following
_scrollController = ScrollController()
..addListener(
(){
if(_isAppBarExpanded){
if(_theme != Colors.white){
setState(() {
_theme = Colors.white;
print('setState is called with white');
});
}
}else{
if(_theme != Colors.black){
setState(() {
_theme = Colors.black;
print('setState is called with black');
});
}
}
}
Use NestedScrollView that have innerBoxIsScrolled boolean flag that will be a decent solution for your problem something like this below
NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
print("INNEER SCROLLED VI=======>$innerBoxIsScrolled");
return <Widget>[
SliverAppBar(),
]},
body:Center(child:Text("Test IT")),
);
You can use SliverLayoutBuilder to get the current SliverConstraints and read its value, to easily detect how much scrolling has occurred. This is very similar to LayoutBuilder except it's operating in the sliver-world.
If constraints.scrollOffset > 0, that means the user has scrolled at least a little bit. Using this method, if you want to do some animation/transition when scrolling, it's easy too - just get the current scrollOffset and generate your animation frame based on that.
For example, this SliverAppBar changes color when user scrolls:
SliverLayoutBuilder(
builder: (BuildContext context, constraints) {
final scrolled = constraints.scrollOffset > 0;
return SliverAppBar(
title: Text('Sliver App Bar'),
backgroundColor: scrolled ? Colors.blue : Colors.red,
pinned: true,
);
},
)
This works for me
check this line
title: Text(title,style: TextStyle(color: innerBoxIsScrolled? Colors.black:Colors.white),),
change your title to
title: innerBoxIsScrolled? Text("hello world") : Text("Good Morning",)
class _ProductsState extends State<Products> {
String image;
String title;
_ProductsState(this.image,this.title);
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled){
return <Widget>[
SliverOverlapAbsorber(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
leading: InkWell(
onTap: (){
},
child: Icon(
Icons.arrow_back_ios,
color: Colors.black,
),
),
backgroundColor: Colors.white,
pinned: true,
//floating: true,
stretch: true,
expandedHeight: 300.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(title,style: TextStyle(color: innerBoxIsScrolled? Colors.black: Colors.white),),
background: CachedNetworkImage(imageUrl:image,fit: BoxFit.fitWidth,alignment: Alignment.topCenter,
placeholder: (context, url) => const CircularProgressIndicator(color: Colors.black,),
errorWidget: (context, url, error) => const Icon(Icons.error),),
),
),
),
];
},
body: Builder(
builder:(BuildContext context) {
return CustomScrollView(
slivers: [
SliverOverlapInjector(
// This is the flip side of the SliverOverlapAbsorber above.
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
),
SliverToBoxAdapter(
child: Container(
height: 90,
color: Colors.black,
),
),
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.red,
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
color: Colors.green,
),
),
),
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.blue,
),
),
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.red,
),
),
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.blue,
),
),
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.red,
),
),
],
);
}
),
),
);
}
}