简体   繁体   中英

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:

enter image description here

在此处输入图像描述

在此处输入图像描述

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,
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),`
 shape: ContinuousRectangleBorder(
              borderRadius: BorderRadius.only(
                  bottomLeft: Radius.circular(30),
                  bottomRight: Radius.circular(30))),

Here is your code. Put it inside sliverAppBar

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.

结果 gif 显示应用栏在 Windows 上从展开到折叠再返回时的过渡。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM