Shuai Cheng
Shuai Cheng

Reputation: 141

How to use AnimatedSwitcher and CustomScrollView together

I want switch status with animation in CustomScrollView, but it throw error.

class SliverAnimatedSwitcher extends StatefulWidget {
  final state;

  const SliverAnimatedSwitcher({Key key, this.state}) : super(key: key);

  @override
  _SliverAnimatedSwitcherState createState() => _SliverAnimatedSwitcherState();
}

class _SliverAnimatedSwitcherState extends State<SliverAnimatedSwitcher> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            title: Text('SliverAnimatedSwitcher'),
          ),
          _buildContent(),
        ],
      ),
    );
  }

  get state => widget.state;

  Widget _buildContent() {
    var content;
    if (state.isNotEmpty == true) {
      content = SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            var item = state.items[index];
            return ListTile(
              title: Text(item.title),
            );
          },
          childCount: state.items.length,
        ),
      );
    } else if (state.isError) {
      content = SliverFillRemaining(
        key: Key('error'),
        child: Container(alignment: Alignment.center, child: Text('Error')),
      );
    } else if (state.isLoading) {
      content = SliverFillRemaining(
        key: Key('loading'),
        child: Container(alignment: Alignment.center, child: Text('Loading')),
      );
    } else {
      content = SliverFillRemaining(
        key: Key('empty'),
        child: Container(alignment: Alignment.center, child: Text('Empty')),
      );
    }
    return AnimatedSwitcher(
      duration: Duration(milliseconds: 300),
      child: content,
    );
  }
}

Upvotes: 13

Views: 3467

Answers (3)

Michael Marsh
Michael Marsh

Reputation: 1

I created the following class the resolve the same issue for my personal project. It should work here, although it can definitely be improved. The main issue is that Flutter does not have a SliverStack, so my class is more of a workaround. Just wrap each sliver widget with the following class and provide opposing booleans for each:

import 'package:flutter/widgets.dart';

/// A sliver that animates its visibility.
class SliverAnimatedVisibility extends StatelessWidget {
  /// Creates a new [SliverAnimatedVisibility].
  const SliverAnimatedVisibility({
    super.key,
    this.curve = Curves.fastOutSlowIn,
    this.duration = const Duration(milliseconds: 350),
    required this.visible,
    required this.sliver,
  });

  /// The curve to use for the animation.
  final Curve curve;

  /// The amount of time occupied by the animation.
  final Duration duration;

  /// Whether the child is visible.
  final bool visible;

  /// The sliver to display.
  final Widget sliver;

  @override
  Widget build(BuildContext context) {
    return SliverAnimatedOpacity(
      opacity: visible ? 1 : 0,
      curve: curve,
      duration: duration,
      sliver: SliverVisibility(
        visible: visible,
        sliver: sliver,
      ),
    );
  }
}

I wrote this in 2-3 minutes, so feel free to update the code as you need! 😄

Upvotes: 0

Code on the Rocks
Code on the Rocks

Reputation: 17616

There's a sliver_tools package on pub.dev that has a SliverAnimatedSwitcher. You'd use it like this:

SliverAnimatedSwitcher(
  duration: kThemeAnimationDuration,
  child: content,
)

Upvotes: 7

yellowgray
yellowgray

Reputation: 4499

It is because sliver widget is not capable with Stack which is using as layout builder inside AnimatedSwitcher.

I found a temporary solution. Though the result is not equal to the original one (only show the last widget in the result), but I think it is acceptable for me now with such little effort.

  1. First create a SliverAnimatedSwitcher exact the same code as AnimatedSwitcher here
  2. Modify the Stack part to return the currentChild only (defaultLayoutBuilder)
  3. Change FadeTransition to SliverFadeTransition (defaultTransitionBuilder)

.

static Widget defaultLayoutBuilder(Widget currentChild, List<Widget> previousChildren) {
  return currentChild;
}

.

static Widget defaultTransitionBuilder(Widget child, Animation<double> animation) {
  return SliverFadeTransition(
    opacity: animation,
    sliver: child,
  );
}

If someone can find a way to Stack Sliver widgets above each other, the result may be more perfect.

Upvotes: 3

Related Questions