'Do not want rounded corners in the AppBar when the Sliver App Bar is collapsed
I'm trying to implement a layout, where the Sliver App Bar has rounded bottom corners when expanded, but when it is collapsed I do not want those rounded corners.
Actual Behaviour:
Expected Behaviour:
Here's my SliverAppBar code:
`SliverAppBar(
systemOverlayStyle: const SystemUiOverlayStyle(
statusBarColor: Color(0xFFE0E64B),
),
backgroundColor: Color(0xFFE0E64B),
expandedHeight: 300.0,
floating: false,
pinned: true,
collapsedHeight: 60.0,
onStretchTrigger: () async {
setState(() {});
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'Pokedex',
style: TextStyle(
color: Colors.white,
),
),
Text(
'#025',
style: TextStyle(
color: Colors.white,
),
),
],
),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
background: Container(
decoration: const BoxDecoration(
color: Color(0xFFE0E64B),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(50.0),
bottomRight: Radius.circular(50.0),
),
),
child: Hero(
tag: 'pokemon_container$index',
child: Column(
children: [
const SizedBox(
height: 120.0,
),
Expanded(
child: ClipRRect(
child: Image.network(
imageUrl,
fit: BoxFit.scaleDown,
),
),
),
const SizedBox(
height: 30.0,
),
],
),
),
),
),
),`
Solution 1:[1]
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))),
Here is your code. Put it inside sliverAppBar
Solution 2:[2]
NestedScrollView / SliverAppBar solution
This is definitely achievable. SliverAppBar does support what we need, it has support for rounded borders, the shadow effect and changing sizes. For handling the border requirement we can use a RoundedRectangleBorder.
Although for getting a smooth transition for the border change, we need to update the values frequently, when changing the size of the SliverAppBar.
Example code
Do note that the package flutter_riverpod (version 1.0.3) is used for state management in this example.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class RoundedSliverExampleScreen extends StatelessWidget {
const RoundedSliverExampleScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
ExpandingAppBar(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Flexible is important for the children widgets added here.
Flexible(child: Container(color: Colors.yellow, width: 50, height: 50,))
],
)
];
},
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text("Hello!")
],
),
)
);
}
}
/// An SliverAppBar widget with alternating rounded border depending on the
/// expandedHeight.
///
/// Provides easy support for adding children widgets in the
/// expanded area as if it was a Column, although children widgets should be
/// wrapped in a Flexible widget.
class ExpandingAppBar extends ConsumerWidget {
const ExpandingAppBar({
Key? key,
this.children = const <Widget>[],
this.mainAxisAlignment = MainAxisAlignment.start
}) : super(key: key);
final List<Widget> children;
final MainAxisAlignment mainAxisAlignment;
@override
Widget build(BuildContext context, WidgetRef ref) {
RoundedHeaderState state = ref.watch(roundedHeaderProvider);
return SliverAppBar(
expandedHeight: state.highestHeight,
pinned: true,
primary: true,
forceElevated: true,
title: const Text('Pokèdex'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(state.radius)),
),
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// We update the state here.
ref.read(roundedHeaderProvider.notifier).updateHeight(constraints.maxHeight);
return Opacity(
opacity: state.scrollFraction,
child: Padding(
padding: EdgeInsets.only(top: state.smallestHeight),
child: Column(mainAxisAlignment: mainAxisAlignment, children: children),
),
);
},
),
);
}
}
@immutable
class RoundedHeaderState {
final double highestHeight = 256;
final double smallestHeight = kToolbarHeight + 24;
final double currentHeight;
final double contentOpacity = 1;
const RoundedHeaderState({this.currentHeight = 256});
double get scrollFraction => min(max((currentHeight - smallestHeight) / (highestHeight - smallestHeight), 0), 1);
double get radius => 64 * scrollFraction;
}
class RoundedHeaderNotifier extends StateNotifier<RoundedHeaderState> {
RoundedHeaderNotifier(): super(const RoundedHeaderState());
updateHeight(double currentHeight) {
final newState = RoundedHeaderState(currentHeight: currentHeight);
// Check that the new state is not equal to the next (prevents rebuild loop)
if(state.currentHeight != newState.currentHeight) {
// Setting state triggers an rebuild, the PostFrameCallback let Flutter
// postpone the upcoming rebuild at a later time.
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
state = newState;
});
}
}
}
final roundedHeaderProvider = StateNotifierProvider<RoundedHeaderNotifier, RoundedHeaderState>((ref) {
return RoundedHeaderNotifier();
});
// Pay attention to the ProviderScope wrapping the MaterialApp. Riverpod requires this.
void main() => runApp(
const ProviderScope(
child: MaterialApp(home: RoundedSliverExampleScreen())
)
);
Result - Gif of the SliverAppBar's transition.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Tolga Y?lmaz |
| Solution 2 | Tor-Martin Holen |





