Search code examples
flutterwidgetdropdown

Dropdown for FAQ for flutter


Hey can anyone direct me to a widget or library in flutter which is similar to the image I have provided. I want this for a FAQ section. Initially only question should be visible, on tapping the component it should show answer.

enter image description here


Solution

  • I would either go with this library or if you want, here is a snippet of the widget I created for my own purposes:

    import 'package:flutter/material.dart';
    
    
    /// [CustomCollapsable] is collapsable widget which shows a [preview]
    /// and a trailing with a [trailingText] by default. On tap the trailing or
    /// its [trailingText], it animates to show the [child].
    
    /// NOTE: [ExpansionTile] doesn't support layout changes, for example
    /// moving trailing down or to hide the [preview] after expand the content.
    /// [CustomCollapsable] is inspired on [ExpansionTile] with additional features.
    class CustomCollapsable extends StatefulWidget {
      /// [Widget] shown when expanded
      final Widget child;
    
      /// [Widget] shown only when it is not expanded
      final Widget preview;
    
      /// [String] that goes on top of the trailing arrow
      final String trailingText;
    
      /// Callback [ValueChanged] function fired after animation
      final ValueChanged<bool> onExpansionChanged;
    
      const CustomCollapsable({
        Key key,
        @required this.preview,
        @required this.child,
        @required this.trailingText,
        this.onExpansionChanged,
      });
    
      @override
      _CustomCollapsableState createState() => _CustomCollapsableState();
    }
    
    class _CustomCollapsableState extends State<CustomCollapsable>
        with SingleTickerProviderStateMixin {
      static final Animatable<double> _easeInTween = CurveTween(
        curve: Curves.easeIn,
      );
      static final Animatable<double> _halfTween = Tween<double>(
        begin: 0.0,
        end: 0.5,
      );
    
      AnimationController _controller;
      Animation<double> _heightFactor;
      Animation<double> _iconTurns;
    
      bool _isExpanded = false;
    
      @override
      void initState() {
        super.initState();
    
        _controller = AnimationController(
          duration: Duration(seconds: 1),
          vsync: this,
        );
        _iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
    
        _heightFactor = _controller.drive(_easeInTween);
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      bool get isClosed => !_isExpanded && _controller.isDismissed;
    
      void _handleTap() {
        setState(() {
          _isExpanded = !_isExpanded;
    
          if (_isExpanded) {
            _controller.forward();
          } else {
            _controller.reverse().then<void>((void value) {
              if (!mounted) return;
              setState(() {});
            });
          }
    
          PageStorage.of(context)?.writeState(context, _isExpanded);
        });
    
        if (widget.onExpansionChanged != null) {
          widget.onExpansionChanged(_isExpanded);
        }
      }
    
      Widget _buildTralling() {
        return GestureDetector(
          onTap: _handleTap,
          child: Padding(
            padding: const EdgeInsets.only(top: 15.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                isClosed
                    ? Text(widget.trailingText.toUpperCase())                      
                    : const SizedBox(),
                RotationTransition(
                  turns: _iconTurns,
                  child: const Icon(Icons.expand_more),
                ),
              ],
            ),
          ),
        );
      }
    
      Widget _buildChild(BuildContext context, Widget child) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            // Preview widget is visible only when the parent is closed
            isClosed ? widget.preview : const SizedBox(),
            ClipRect(
              child: Align(
                heightFactor: _heightFactor.value,
                child: child,
              ),
            ),
            _buildTralling()
          ],
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: _controller.view,
          builder: _buildChild,
          child: isClosed ? null : widget.child,
        );
      }
    }
    

    You can define child and preview widgets to your needs.