How can I make a SliverAppBar that is similar to Google News? - flutter

I want a standard AppBar when the app starts, however, once the user begins scrolling I want the app bar to slide up and off the screen. However, when that happens, the status bar (on iOS) doesn't have a background. I don't want to set a constant background directly to the status bar using something like flutter_statusbarcolor because that leaves a solid color, which isn't a widget.
The only solution I have right now is to just keep the AppBar pined.
However, what I really want to do is what Google News does. The AppBar slides up almost all the way, however, it stays under the app bar with some opacity.
How Google News does it:
Is there any way to do this with Flutter and SliverAppBar without doing it a hacky way? The only thing I'm thinking is doing it with a stack, however, how would I know how to keep it under the status bar, as Android already has some opacity under the status bar.

Scroll widgets in flutter have controllers that inform what's going on with the scroll. You can get how much the sliver has been scrolled and change it's opacity, content and size accordingly. It's not going to be simple though, but take a look at ScrollController, used by the CustomScrollView.

I used OrientationBuilder widget to listen to changes in orientation, as when there are changes to the orientation the status bar hight can change.
Then I used FlutterStatusbarManager package to get the height of the status bar. FlutterStatusbarManager.getHeight is a future so it needed to be wrapped with a FutureBuilder
Here is the full example code
import 'package:flutter/material.dart';
import 'package:flutter_statusbar_manager/flutter_statusbar_manager.dart';
class FixedStatusbarBackground extends StatelessWidget {
final Widget child;
const FixedStatusbarBackground({Key key, this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
child,
OrientationBuilder(
builder: (context, Orientation orientation) => FutureBuilder<double>(
future: FlutterStatusbarManager.getHeight,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
height: snapshot.data,
color: Theme.of(context)
.appBarTheme
.color
.withOpacity(.7) //Colors.white.withOpacity(.7),
);
} else {
return Container();
}
}),
),
],
);
}
}
The child widget that is passed in is the entire CustomScrollView.

Related

Scroll position lost if tab changed while scrolling animation is still ongoing

I have 3 views which are accessible via the bottom navigation tab. Each view has its own ListView, which looks like this:
// primary = bottomTabNavigation.index //
ListView(
controller: primary ? null : scrollController,
key: const PageStorageKey<String>('view1'),
primary: primary,
physics: primary
? AlwaysScrollableScrollPhysics()
: NeverScrollableScrollPhysics(),
children: const [
Text("A"),
SizedBox(height: 1000),
Text("B"),
],
),
If I start a big swipe on view1, and switch to view2 via bottom tab navigator, the scroll position when I come back to view1 is still at the top. Somehow, the scroll position only saves upon the scrolling animation completing.
Is there some way to switch tabs and store the last position (without waiting for animation)?
Create a Key outside the build method
final _key = GlobalKey();
Step 1: make your widgets staefulWidget.
Step 2: now use AutomaticKeepAliveClientMixin using with keyword.
class _DealListState extends
State<DealList> with
AutomaticKeepAliveClientMixin<DealList>
{
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
// your current widget build
}}
It will keep your listview and other states when you moves from one page to another.
Note: if it's impossible to change every page to stateful widget then just make a new StatefulWidget that use AutomaticKeepAliveClientMixin and will take a child widget from outside and now you can use this widget to wrap your already present widget and can be used through the app.

Flutter pull to refresh on lower part of screen

I have a app that looks like the picture above.
When i swipe right or left, page switches along with the pageview content. but NOT the image part, it stays still.
When i implement pull to refresh on this case, the gap opens above image part showing progress indicator and refreshes.
I want the refresher crack open between image and content part, how do i achieve this?
thank you so much for reply in advance, you are the hero.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
const Expanded(
child: SizedBox(height: 200), //The 'Image Part'
),
SmartRefresher(
child: PageView.builder(itemBuilder: (context, state) => Column(
children:[SizedBox(height:200),PageContent()], itemCount: 3),//The 'Content Part'
)
],
));
}
}
Example code added above, this represents what i would like to implement, I want the refresher only accessible inside PageContent()
but i could not because it contains Column inside
seems like an issue with your stack.
if you want the image part to also follow the swipe it has to be within the pageview.builder.
For the refresh, under pageview.builder wrap the content part in the pull-to-refresh widget but not the image.
This is possibly caused by the Stack widget
Change your Stack to a Column
Wrap your SmartRefresher with and Expanded widget for your ListviewBuilder to take the remaining space in vertical axis. You will have an unbounded high error otherwise
Set the shrinkwrap of your ListviewBuilder to true for it to build your list elements on demand
That's it! ☑️

Animated screen between a navigation of two screens in flutter

I have three screens.
FirstScreen, SecondScreen and a GreenScreen,
im using custom navigation in my navigation routes like so (im using named routes),
case secondScreenUIRoute:
return Platform.isIOS
? CupertinoPageRoute(
builder: (context) => const SecondScreen())
: CustomPageRoute(
builder: (context) => const SecondScreen());
my green screen looks like so
class GreenScreen extends StatelessWidget {
const GreenScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: CustomColors.greenLight,
);
}
}
My basic idea was to have this GreenScreen in between FirstScreen and SecondScreen on navigation(from first to second), but as a fade in fade out effect. Plainly I like to give the user an impression like, when going from FirstScreen to SecondScreen, there seems to be a subtle animation where a green screen is faded in and faded out before reaching SecondScreen.
how Can i achieve this in flutter?
you can use animations package:
https://pub.dev/packages/animations#fade-through
This package contains pre-canned animations for commonly-desired effects. The animations can be customized with your content and dropped into your application to delight your users.
this is an example source code of using the package:
https://github.com/flutter/packages/tree/master/packages/animations/example
if you want to learn from youtube, you can learn here:
https://www.youtube.com/watch?v=3GMq45zRVLo
I've used this package before, and I recommend this package because it's easy to use and this package was made by the flutter team

Flutter - What's the best practice to create a fixed AppBar during navigation

In native android development, it is common to use FragmentTransaction to create a navigation animation, where the actionBar's position stays fixed (but actionBar's content changed), and the fragment beneath actionBar performs a transition animation (like slide in or out).
To put it simple, the AppBar and the body performs different transition animation. In flutter, what is the best practice to create such animation?
Currently I can think of a solution of using a navigation in Scaffold.body and using Stream + StreamBuilder to start AppBar redraw. Something like the following code.
Scaffold(
appBar: StreamBuilder<Int>(
stream: Bloc.of(context).currentFragmentId
builder: //Some logic to decide which AppBar is appropriate
),
body: Navigator(
//Navigation between fragments
),
)
But this is really weird. So is there a best practice to do this? Please let me know!
Well, since there is currently no answer availble. I'm going to share my solution here, though it is not so elegant.
The solution is to wrap AppBar in a Hero widget. Since Scaffold only takes a PreferedSize widget as an appbar, some customization is required.
class HeroAppBar extends StatelessWidget implements PreferredSizeWidget {
//...
#override
final Size preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0));
//...
#override
Widget build(BuildContext context) {
return Hero(
tag: tag,
child: AppBar(
//...
),
);
}
}
And make sure your Navigator implemented the HeroController. (Default Navigator has already implemented it)

Flutter: Use Navigator with TabBar and TabBarView

I'm trying to understand the class Navigator.
A Navigator Route seem to always return a new widget but what if I want to manage the TabBar and TabBarView so that each Tab when tapped or swipe on, will be pushed to the Navigator stack, I don't find a what to do that.
On a more general case, can I react to a route change without creating a new widget but instead taking another action like scrolling to a specific item in a listView?
I've tried recreating the entire app structure every time but doing this way I don't have the nice default animation and, also, doesn't seem a good approach to me.
You can use WillPopScope widget and its onWillPop to catch the back button pressure and handle it yourself. Find more info here https://docs.flutter.io/flutter/widgets/WillPopScope-class.html
On a more general case, can I react to a route change without creating
a new widget but instead taking another action like scrolling to a
specific item in a listView?
This looks more specific rather than general. However, you do need to set a ScrollController in your ListView and let it scroll the list for you to the desired point. A simple example function returning to top:
class MyFancyClass extends StatelessWidget{
...
ScrollController _scrollController;
...
#override
Widget build(BuildContext Context){
return ....
new ListView(
...
controller: _scrollController,
...
}
void _toTop() {
_scrollController.animateTo(
0.0,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
}
Check https://docs.flutter.io/flutter/widgets/ScrollController-class.html for more details and behaviors. In case you want the back button to bring you to the top:
Widget build(BuildContext context) {
return new WillPopScope(
onWillPop: _onTop,
child:
...
),
);
}
Concerning the tab behavior I suggest you to read https://docs.flutter.io/flutter/material/TabController-class.html to better understand how to implement what you have in your mind.