Skip to content

Instantly share code, notes, and snippets.

@DaisukeNagata
Created July 30, 2023 03:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaisukeNagata/5cd75375dc33860073f2bb674965107e to your computer and use it in GitHub Desktop.
Save DaisukeNagata/5cd75375dc33860073f2bb674965107e to your computer and use it in GitHub Desktop.
not change example
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
const Duration _kExpand = Duration(milliseconds: 300);
final counterProvider =
StateNotifierProvider<CounterNotifier, CounterState>((ref) {
return CounterNotifier();
});
final itemProvider =
StateNotifierProvider<ItemNotifier, List<Item>>((ref) => ItemNotifier());
class ItemNotifier extends StateNotifier<List<Item>> {
ItemNotifier() : super([]);
void addItem(Item item) {
state = [...state, item];
}
void toggleIsExpanded(int index) {
var updatedItem =
state[index].copyWith(isExpanded: !state[index].isExpanded);
state = [
...state.sublist(0, index),
updatedItem,
...state.sublist(index + 1),
];
}
}
class Item {
Item({
required this.expandedValue,
required this.headerValue,
this.isExpanded = false,
});
final String expandedValue;
final String headerValue;
final bool isExpanded;
Item copyWith({
String? expandedValue,
String? headerValue,
bool? isExpanded,
}) {
return Item(
expandedValue: expandedValue ?? this.expandedValue,
headerValue: headerValue ?? this.headerValue,
isExpanded: isExpanded ?? this.isExpanded,
);
}
}
class CounterState {
int count;
bool isLoading;
CounterState(this.count, this.isLoading);
}
class CounterNotifier extends StateNotifier<CounterState> {
CounterNotifier() : super(CounterState(0, false));
void increment() {
state = CounterState(state.count + 1, false);
}
Future<void> incrementAfterDelay() async {
state = CounterState(state.count, true);
await Future.delayed(const Duration(seconds: 1));
increment();
}
}
void main() => runApp(const ProviderScope(child: CounterApp()));
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends ConsumerWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(
title: GestureDetector(
child: const Text('CounterPage'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondCounterPage(ref: ref)),
);
},
),
),
body: Center(
child: state.isLoading
? const CircularProgressIndicator()
: Text('Count: ${state.count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).incrementAfterDelay();
ref.read(itemProvider.notifier).addItem(Item(
expandedValue: 'Expanded${state.count}',
headerValue: 'Header${state.count}',
isExpanded: true,
));
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class SecondCounterPage extends HookWidget {
const SecondCounterPage({super.key, required this.ref});
final WidgetRef ref;
@override
Widget build(BuildContext context) {
final itemNotifier = ref.watch(itemProvider.notifier);
final items = ref.watch(itemProvider);
final isLoading = useState(false);
final state = ref.watch(counterProvider);
void incrementCounter() async {
isLoading.value = true;
ref.read(counterProvider.notifier).incrementAfterDelay();
await Future.delayed(const Duration(seconds: 3));
isLoading.value = false;
final newItem = Item(
expandedValue: 'Expanded${state.count}',
headerValue: 'Header${state.count}',
isExpanded: true,
);
itemNotifier.addItem(newItem);
isLoading.value = false;
}
return Scaffold(
appBar: AppBar(
title: const Text('SecondCounterPage'),
),
body: Center(
child: isLoading.value
? const CircularProgressIndicator()
: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = itemNotifier.debugState[index];
return ExpandetWidget(
height: 50.0,
padding: const EdgeInsets.only(
left: 24,
right: 24,
),
childrenPadding:
const EdgeInsets.symmetric(horizontal: 24.0),
index: index,
titleList: item.expandedValue,
controlAffinity: null,
gestureTapCallback: () {
itemNotifier.toggleIsExpanded(index);
},
title: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(item.headerValue),
const Text('サブタイトル', textAlign: TextAlign.left),
Icon(
item.isExpanded <- not change
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_up,
),
],
),
),
trailing: null, // 矢印を表示しない
children: item.isExpanded
? [
Text(item.expandedValue),
Icon(
item.isExpanded <- not change
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_up,
),
]
: [],
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class ExpandetWidget extends StatefulWidget {
const ExpandetWidget({
super.key,
this.leading,
required this.title,
required this.height,
required this.padding,
required this.index,
required this.titleList,
required this.gestureTapCallback,
this.subtitle,
this.onExpansionChanged,
this.children = const <Widget>[],
this.trailing,
this.initiallyExpanded = false,
this.maintainState = false,
this.tilePadding,
this.expandedCrossAxisAlignment,
this.expandedAlignment,
this.childrenPadding,
this.backgroundColor,
this.collapsedBackgroundColor,
this.textColor,
this.collapsedTextColor,
this.iconColor,
this.collapsedIconColor,
this.controlAffinity,
}) : assert(
expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
'error',
);
final Widget? leading;
final Widget title;
final double height;
final EdgeInsets padding;
final int index;
final String titleList;
final GestureLongPressCallback gestureTapCallback;
final Widget? subtitle;
final ValueChanged<bool>? onExpansionChanged;
final List<Widget> children;
final Color? backgroundColor;
final Color? collapsedBackgroundColor;
final Widget? trailing;
final bool initiallyExpanded;
final bool maintainState;
final EdgeInsetsGeometry? tilePadding;
final Alignment? expandedAlignment;
final CrossAxisAlignment? expandedCrossAxisAlignment;
final EdgeInsetsGeometry? childrenPadding;
final Color? iconColor;
final Color? collapsedIconColor;
final Color? textColor;
final Color? collapsedTextColor;
final ListTileControlAffinity? controlAffinity;
@override
State<ExpandetWidget> createState() => _ExpandetWidgetState();
}
class _ExpandetWidgetState extends State<ExpandetWidget>
with SingleTickerProviderStateMixin {
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0, end: 0.5);
final ColorTween _borderColorTween = ColorTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
late AnimationController _controller;
late Animation<double> _iconTurns;
late Animation<double> _heightFactor;
late Animation<Color?> _headerColor;
late Animation<Color?> _iconColor;
late bool _isExpanded = true;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_heightFactor = _controller.drive(_easeInTween);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _handleTap() {
setState(() {
if (_isExpanded) {
_isExpanded = !_isExpanded;
widget.gestureTapCallback();
_controller.forward();
} else {
_controller.reverse().whenComplete(() => {
_isExpanded = !_isExpanded,
widget.gestureTapCallback(),
});
}
});
}
// Platform or null affinity defaults to trailing.
ListTileControlAffinity _effectiveAffinity(
ListTileControlAffinity? affinity,
) {
switch (affinity ?? ListTileControlAffinity.trailing) {
case ListTileControlAffinity.leading:
return ListTileControlAffinity.leading;
case ListTileControlAffinity.trailing:
case ListTileControlAffinity.platform:
return ListTileControlAffinity.trailing;
}
}
Widget? _buildIcon(BuildContext context) {
return RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
);
}
Widget? _buildLeadingIcon(BuildContext context) {
if (widget.controlAffinity == null) {
return null;
}
if (_effectiveAffinity(widget.controlAffinity) !=
ListTileControlAffinity.leading) {
return null;
}
return _buildIcon(context);
}
Widget? _buildTrailingIcon(BuildContext context) {
if (widget.controlAffinity == null) {
return null;
}
if (_effectiveAffinity(widget.controlAffinity) !=
ListTileControlAffinity.trailing) {
return null;
}
return _buildIcon(context);
}
Widget _buildChildren(BuildContext context, Widget? child) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: [
ListTileTheme.merge(
iconColor: _iconColor.value,
textColor: _headerColor.value,
child: ListTile(
contentPadding: widget.padding,
leading: widget.leading ?? _buildLeadingIcon(context),
title: widget.title,
subtitle: widget.subtitle,
trailing: widget.trailing ?? _buildTrailingIcon(context),
),
),
Positioned(
left: widget.padding.left,
top: widget.padding.top,
right: widget.padding.right,
bottom: widget.padding.bottom,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _handleTap,
borderRadius: BorderRadius.circular(0.0),
),
),
),
],
),
ClipRect(
child: Align(
alignment: widget.expandedAlignment ?? Alignment.center,
heightFactor: _heightFactor.value,
child: child,
),
),
],
);
}
@override
void didChangeDependencies() {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
_borderColorTween.end = theme.dividerColor;
_headerColorTween
..begin = widget.collapsedTextColor ?? theme.textTheme.titleMedium!.color
..end = widget.textColor ?? colorScheme.primary;
_iconColorTween
..begin = widget.collapsedIconColor ?? theme.unselectedWidgetColor
..end = widget.iconColor ?? colorScheme.primary;
_backgroundColorTween
..begin = widget.collapsedBackgroundColor
..end = widget.backgroundColor;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final closed = !_isExpanded && _controller.isDismissed;
final shouldRemoveChildren = closed && !widget.maintainState;
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: GestureDetector(
onTap: _handleTap,
child: shouldRemoveChildren
? null
: Offstage(
offstage: closed,
child: Padding(
padding: widget.childrenPadding ?? EdgeInsets.zero,
child: Column(
crossAxisAlignment: widget.expandedCrossAxisAlignment ??
CrossAxisAlignment.center,
children: widget.children,
),
),
),
),
);
},
);
}
}
@DaisukeNagata
Copy link
Author

no change arrow

2023-07-30.12.32.30.mov

@DaisukeNagata
Copy link
Author

DaisukeNagata commented Jul 31, 2023

this is refactor

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

const Duration _kExpand = Duration(milliseconds: 300);

final counterProvider =
    StateNotifierProvider<CounterNotifier, CounterState>((ref) {
  return CounterNotifier();
});

final itemProvider =
    StateNotifierProvider<ItemNotifier, List<Item>>((ref) => ItemNotifier());

class ItemNotifier extends StateNotifier<List<Item>> {
  ItemNotifier() : super([]);
  void addItem(Item item) {
    state = [...state, item];
  }

  void toggleIsExpanded(int index) {
    var updatedItem =
        state[index].copyWith(isExpanded: !state[index].isExpanded);
    state = [
      ...state.sublist(0, index),
      updatedItem,
      ...state.sublist(index + 1),
    ];
  }

  void collapseAll() {
    state = state.map((item) => item.copyWith(isExpanded: true)).toList();
  }
}

class Item {
  Item({
    required this.expandedValue,
    required this.headerValue,
    this.isExpanded = false,
  });
  final String expandedValue;
  final String headerValue;
  final bool isExpanded;
  Item copyWith({
    String? expandedValue,
    String? headerValue,
    bool? isExpanded,
  }) {
    return Item(
      expandedValue: expandedValue ?? this.expandedValue,
      headerValue: headerValue ?? this.headerValue,
      isExpanded: isExpanded ?? this.isExpanded,
    );
  }
}

class CounterState {
  int count;
  bool isLoading;
  CounterState(this.count, this.isLoading);
}

class CounterNotifier extends StateNotifier<CounterState> {
  CounterNotifier() : super(CounterState(0, false));
  void increment() {
    state = CounterState(state.count + 1, false);
  }

  Future<void> incrementAfterDelay() async {
    state = CounterState(state.count, true);
    await Future.delayed(const Duration(seconds: 1));
    increment();
  }
}

void main() => runApp(const ProviderScope(child: CounterApp()));

class CounterApp extends StatelessWidget {
  const CounterApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterPage(),
    );
  }
}

class CounterPage extends ConsumerWidget {
  const CounterPage({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(counterProvider);
    return Scaffold(
      appBar: AppBar(
        title: GestureDetector(
          child: const Text('CounterPage'),
          onTap: () {
            ref.read(itemProvider.notifier).collapseAll();
            Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => SecondCounterPage(ref: ref)),
            );
          },
        ),
      ),
      body: Center(
        child: state.isLoading
            ? const CircularProgressIndicator()
            : Text('Count: ${state.count}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).incrementAfterDelay();
          ref.read(itemProvider.notifier).addItem(Item(
                expandedValue: 'Expanded${state.count}',
                headerValue: 'Header${state.count}',
                isExpanded: true,
              ));
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class SecondCounterPage extends HookWidget {
  const SecondCounterPage({super.key, required this.ref});
  final WidgetRef ref;

  @override
  Widget build(BuildContext context) {
    final itemNotifier = ref.watch(itemProvider.notifier);
    final items = ref.watch(itemProvider);
    final isLoading = useState(false);
    final state = ref.watch(counterProvider);
    void incrementCounter() async {
      isLoading.value = true;
      ref.read(itemProvider.notifier).collapseAll();
      ref.read(counterProvider.notifier).incrementAfterDelay();
      await Future.delayed(const Duration(seconds: 3));
      final newItem = Item(
        expandedValue: 'Expanded${state.count}',
        headerValue: 'Header${state.count}',
        isExpanded: true,
      );
      itemNotifier.addItem(newItem);
      isLoading.value = false;
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('SecondCounterPage'),
      ),
      body: Center(
        child: isLoading.value
            ? const CircularProgressIndicator()
            : ListView.builder(
                itemCount: items.length,
                itemBuilder: (context, index) {
                  final item = itemNotifier.debugState[index];
                  return ExpandetWidget(
                    height: 50.0,
                    padding: const EdgeInsets.only(
                      left: 24,
                      right: 24,
                    ),
                    childrenPadding:
                        const EdgeInsets.symmetric(horizontal: 24.0),
                    index: index,
                    titleList: item.expandedValue,
                    controlAffinity: null,
                    gestureTapCallback: () {
                      isLoading.value = true;
                      itemNotifier.toggleIsExpanded(index);
                      isLoading.value = false;
                    },
                    title: Container(
                      decoration: BoxDecoration(
                        color: Colors.white,
                        boxShadow: [
                          BoxShadow(
                            color: Colors.grey.withOpacity(0.5),
                            spreadRadius: 1,
                            blurRadius: 3,
                            offset: const Offset(0, 4),
                          ),
                        ],
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: [
                          Text(item.headerValue),
                          const Text('サブタイトル', textAlign: TextAlign.left),
                          Icon(
                            item.isExpanded
                                ? Icons.keyboard_arrow_down
                                : Icons.keyboard_arrow_up,
                          ),
                        ],
                      ),
                    ),
                    trailing: null, // 矢印を表示しない
                    children: [
                      Text(item.expandedValue),
                      Icon(
                        item.isExpanded
                            ? Icons.keyboard_arrow_down
                            : Icons.keyboard_arrow_up,
                      ),
                    ],
                  );
                },
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class ExpandetWidget extends StatefulWidget {
  const ExpandetWidget({
    super.key,
    this.leading,
    required this.title,
    required this.height,
    required this.padding,
    required this.index,
    required this.titleList,
    required this.gestureTapCallback,
    this.subtitle,
    this.onExpansionChanged,
    this.children = const <Widget>[],
    this.trailing,
    this.initiallyExpanded = false,
    this.maintainState = false,
    this.tilePadding,
    this.expandedCrossAxisAlignment,
    this.expandedAlignment,
    this.childrenPadding,
    this.backgroundColor,
    this.collapsedBackgroundColor,
    this.textColor,
    this.collapsedTextColor,
    this.iconColor,
    this.collapsedIconColor,
    this.controlAffinity,
  }) : assert(
          expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
          'error',
        );
  final Widget? leading;
  final Widget title;
  final double height;
  final EdgeInsets padding;
  final int index;
  final String titleList;
  final GestureTapCallback gestureTapCallback;
  final Widget? subtitle;
  final ValueChanged<bool>? onExpansionChanged;
  final List<Widget> children;
  final Color? backgroundColor;
  final Color? collapsedBackgroundColor;
  final Widget? trailing;
  final bool initiallyExpanded;
  final bool maintainState;
  final EdgeInsetsGeometry? tilePadding;
  final Alignment? expandedAlignment;
  final CrossAxisAlignment? expandedCrossAxisAlignment;
  final EdgeInsetsGeometry? childrenPadding;
  final Color? iconColor;
  final Color? collapsedIconColor;
  final Color? textColor;
  final Color? collapsedTextColor;
  final ListTileControlAffinity? controlAffinity;
  @override
  State<ExpandetWidget> createState() => _ExpandetWidgetState();
}

class _ExpandetWidgetState extends State<ExpandetWidget>
    with SingleTickerProviderStateMixin {
  static final Animatable<double> _easeInTween =
      CurveTween(curve: Curves.easeIn);
  static final Animatable<double> _halfTween =
      Tween<double>(begin: 0, end: 0.5);
  final ColorTween _borderColorTween = ColorTween();
  final ColorTween _headerColorTween = ColorTween();
  final ColorTween _iconColorTween = ColorTween();
  final ColorTween _backgroundColorTween = ColorTween();
  late AnimationController _controller;
  late Animation<double> _iconTurns;
  late Animation<double> _heightFactor;
  late Animation<Color?> _headerColor;
  late Animation<Color?> _iconColor;
  late bool _isExpanded = true;
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(duration: _kExpand, vsync: this);
    _heightFactor = _controller.drive(_easeInTween);
    _iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
    _headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
    _iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _handleTap() {
    setState(() {
      if (_isExpanded) {
        _isExpanded = !_isExpanded;

        _controller.forward().whenCompleteOrCancel(() {
          widget.gestureTapCallback();
        });
      } else {
        _controller.reverse().whenComplete(() => {
              _isExpanded = !_isExpanded,
              widget.gestureTapCallback(),
            });
      }
    });
  }

// Platform or null affinity defaults to trailing.
  ListTileControlAffinity _effectiveAffinity(
    ListTileControlAffinity? affinity,
  ) {
    switch (affinity ?? ListTileControlAffinity.trailing) {
      case ListTileControlAffinity.leading:
        return ListTileControlAffinity.leading;
      case ListTileControlAffinity.trailing:
      case ListTileControlAffinity.platform:
        return ListTileControlAffinity.trailing;
    }
  }

  Widget? _buildIcon(BuildContext context) {
    return RotationTransition(
      turns: _iconTurns,
      child: const Icon(Icons.expand_more),
    );
  }

  Widget? _buildLeadingIcon(BuildContext context) {
    if (widget.controlAffinity == null) {
      return null;
    }
    if (_effectiveAffinity(widget.controlAffinity) !=
        ListTileControlAffinity.leading) {
      return null;
    }
    return _buildIcon(context);
  }

  Widget? _buildTrailingIcon(BuildContext context) {
    if (widget.controlAffinity == null) {
      return null;
    }
    if (_effectiveAffinity(widget.controlAffinity) !=
        ListTileControlAffinity.trailing) {
      return null;
    }
    return _buildIcon(context);
  }

  Widget _buildChildren(BuildContext context, Widget? child) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Stack(
          alignment: Alignment.center,
          children: [
            ListTileTheme.merge(
              iconColor: _iconColor.value,
              textColor: _headerColor.value,
              child: ListTile(
                contentPadding: widget.padding,
                leading: widget.leading ?? _buildLeadingIcon(context),
                title: widget.title,
                subtitle: widget.subtitle,
                trailing: widget.trailing ?? _buildTrailingIcon(context),
              ),
            ),
            Positioned(
              left: widget.padding.left,
              top: widget.padding.top,
              right: widget.padding.right,
              bottom: widget.padding.bottom,
              child: Material(
                color: Colors.transparent,
                child: InkWell(
                  onTap: _handleTap,
                  borderRadius: BorderRadius.circular(0.0),
                ),
              ),
            ),
          ],
        ),
        ClipRect(
          child: Align(
            alignment: widget.expandedAlignment ?? Alignment.center,
            heightFactor: _heightFactor.value,
            child: child,
          ),
        ),
      ],
    );
  }

  @override
  void didChangeDependencies() {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    _borderColorTween.end = theme.dividerColor;
    _headerColorTween
      ..begin = widget.collapsedTextColor ?? theme.textTheme.titleMedium!.color
      ..end = widget.textColor ?? colorScheme.primary;
    _iconColorTween
      ..begin = widget.collapsedIconColor ?? theme.unselectedWidgetColor
      ..end = widget.iconColor ?? colorScheme.primary;
    _backgroundColorTween
      ..begin = widget.collapsedBackgroundColor
      ..end = widget.backgroundColor;
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    final closed = !_isExpanded && _controller.isDismissed;
    final shouldRemoveChildren = closed && !widget.maintainState;
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        return AnimatedBuilder(
          animation: _controller.view,
          builder: _buildChildren,
          child: GestureDetector(
            onTap: _handleTap,
            child: shouldRemoveChildren
                ? null
                : Offstage(
                    offstage: closed,
                    child: Padding(
                      padding: widget.childrenPadding ?? EdgeInsets.zero,
                      child: Column(
                        crossAxisAlignment: widget.expandedCrossAxisAlignment ??
                            CrossAxisAlignment.center,
                        children: widget.children,
                      ),
                    ),
                  ),
          ),
        );
      },
    );
  }
}


@DaisukeNagata
Copy link
Author

DaisukeNagata commented Jul 31, 2023

2023-07-31.23.50.00.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment