Flutterexperts

Empowering Vision with FlutterExperts' Expertise
Expansible Widget In Flutter

You may have discovered Expansible, a powerful new widget, while experimenting with Flutter 3.32‘s latest capabilities and found that ExpansionTileController is no longer supported.

In this article, we will be Expansible Widget In Flutter. We will learn how to execute a demo program. Let’s examine why this is important and how to use this new widget to create user interfaces that are more adaptable, customisable, and contemporary in your Flutter applications.

If you’re looking for the best Flutter app development company for your mobile application then feel free to contact us at — support@flutterdevs.com.


Table Of Contents:

Reasons for Needing a New Expansion Widget in Flutter:

What Is Unique About Expansible?

Implement Code

Code File

Conclusion



Reasons for Needing a New Expansion Widget in Flutter:

The standard for developing expandable parts, particularly in lists, for many years was Flutter’s ExpansionTile. It was straightforward, had a Material theme, and was simple to integrate into programs.

However, there was a catch: You would run into trouble if you attempted to use Material Design or develop a custom-styled extension component.

Enter: Expansible
The framework presents Expansible, a theme-neutral expansion behaviour building block, with Flutter 3.32. Consider it the engine that drives any type of expandable user interface, regardless of whether you use Material or create your design system.

What Is Unique About Expansible?:

You have complete control with Expansible. With its versatile constructor functions, you may specify how the header appears, how the information animates, and how it is constructed.

Let’s explore it:-

  • Utilise ExpansibleController to Manage Everything:
    Use the new ExpansibleController to programmatically open/close your widget or monitor its status.
final myController = ExpansibleController();

myController.expand();   // Programmatically expand
myController.collapse(); // Programmatically collapse
myController.isExpanded; // Track state

Even better, you can listen for changes:

myController.addListener(() {
print('Expanded: ${myController.isExpanded}');
});
  • Use Case: Custom Expandable Widget:

Expansible is your best buddy if you require a unique layout, design, or animations.

Easy-to-Explain Example

Expansible(
  controller: myController,
  headerBuilder: (context, animation) => MyCustomHeader(animation),
  bodyBuilder: (context, animation) => MyCustomBody(animation),
  expansibleBuilder: (context, header, body, animation) => Column(
    children: [header, body],
  ),
)

What is going on here?

  1. headerBuilder: Specifies the tappable, viewable area.
  2. bodyBuilder: The content that can be expanded.
  3. expansibleBuilder: They are combined, which gives you total layout flexibility.

Implement Code:

Let’s look at a simplified example to see how Expansible functions:

            Expansible(
              controller: _myCustomExpansibleController,
              headerBuilder:
                  (BuildContext context, Animation<double> animation) {
                return GestureDetector(
                  onTap: () {
                    if (_myCustomExpansibleController.isExpanded) {
                      _myCustomExpansibleController.collapse();
                    } else {
                      _myCustomExpansibleController.expand();
                    }
                  },
                  child: Container(
                    decoration: BoxDecoration(
                      color: Colors.deepPurple.shade200,
                      borderRadius: BorderRadius.circular(12.0),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.black.withValues(alpha: 0.1),
                          blurRadius: 4,
                          offset: const Offset(0, 2),
                        ),
                      ],
                    ),
                    padding: const EdgeInsets.all(18.0),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text(
                          'Click Me to Reveal!',
                          style: TextStyle(
                            fontSize: 18.0,
                            fontWeight: FontWeight.w600,
                            color: Colors.deepPurple,
                          ),
                        ),
                        RotationTransition(
                          turns: Tween(
                            begin: 0.0,
                            end: 0.5,
                          ).animate(animation),
                          child: Icon(
                            _myCustomExpansibleController.isExpanded
                                ? Icons.keyboard_arrow_up
                                : Icons.keyboard_arrow_down,
                            color: Colors.deepPurple,
                            size: 28,
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              },
              bodyBuilder: (BuildContext context, Animation<double> animation) {
                return SizeTransition(
                  sizeFactor: animation,
                  axisAlignment: -1.0,
                  child: FadeTransition(
                    opacity: animation,
                    child: Container(
                      margin: const EdgeInsets.only(top: 10.0),
                      padding: const EdgeInsets.all(20.0),
                      decoration: BoxDecoration(
                        color: Colors.deepPurple.shade50,
                        borderRadius: BorderRadius.circular(12.0),
                        border: Border.all(
                          color: Colors.deepPurple.shade100,
                          width: 2,
                        ),
                      ),
                      child: const Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            '🎉 Voila! Here\'s your secret content.',
                            style: TextStyle(
                              fontSize: 16.0,
                              color: Colors.black87,
                            ),
                          ),
                          SizedBox(height: 10.0),
                          Text(
                            'You can put anything here: forms, images, lists, or even other expandable widgets!',
                            style: TextStyle(
                              fontSize: 14.0,
                              color: Colors.grey,
                            ),
                          ),
                          SizedBox(height: 10.0),
                          Row(
                            children: [
                              Icon(Icons.check_circle, color: Colors.green),
                              SizedBox(width: 8),
                              Text('Fully customizable layout'),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                );
              },
              expansibleBuilder:
                  (
                  BuildContext context,
                  Widget header,
                  Widget body,
                  Animation<double> animation,
                  ) {
                return Column(children: [header, body]);
              },
              duration: const Duration(milliseconds: 400),
              curve: Curves.easeInOutQuad,
              reverseCurve: Curves.easeOutCubic,
              maintainState: true, // Keep state even when collapsed
            ),

You can change the layout, animate the icons, and customise the widget’s appearance. That’s what makes Expansible so great.

ExpansionTile now runs on Expansible thanks to Flutter 3.32. This indicates that the previous ExpansionTileController is no longer supported. Do you also want to use buttons to control your tiles?

            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () => _myCustomExpansibleController.expand(),
                  child: const Text('Expand'),
                ),
                ElevatedButton(
                  onPressed: () => _myCustomExpansibleController.collapse(),
                  child: const Text('Collapse'),
                ),
                ElevatedButton(
                  onPressed: () {
                    if (_myCustomExpansibleController.isExpanded) {
                      _myCustomExpansibleController.collapse();
                    } else {
                      _myCustomExpansibleController.expand();
                    }
                  },
                  child: const Text('Toggle'),
                ),
              ],
            ),

Using ExpansionTile or your unique Expansible widget does not affect how it functions. 🎯

Code File:

import 'package:flutter/material.dart';

class ExpansibleDemoPage extends StatefulWidget {
  const ExpansibleDemoPage({super.key});

  @override
  State<ExpansibleDemoPage> createState() => _ExpansibleDemoPageState();
}

class _ExpansibleDemoPageState extends State<ExpansibleDemoPage> {
  late final ExpansibleController _myCustomExpansibleController;

  @override
  void initState() {
    super.initState();
    _myCustomExpansibleController = ExpansibleController();
    _myCustomExpansibleController.addListener(_onExpansionStateChanged);
  }

  void _onExpansionStateChanged() {
    print(
      'Custom Expansible state: ${_myCustomExpansibleController.isExpanded}',
    );
  }

  @override
  void dispose() {
    _myCustomExpansibleController.removeListener(_onExpansionStateChanged);
    _myCustomExpansibleController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Custom Expansible Demo')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: [
            Expansible(
              controller: _myCustomExpansibleController,
              headerBuilder:
                  (BuildContext context, Animation<double> animation) {
                return GestureDetector(
                  onTap: () {
                    if (_myCustomExpansibleController.isExpanded) {
                      _myCustomExpansibleController.collapse();
                    } else {
                      _myCustomExpansibleController.expand();
                    }
                  },
                  child: Container(
                    decoration: BoxDecoration(
                      color: Colors.deepPurple.shade200,
                      borderRadius: BorderRadius.circular(12.0),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.black.withValues(alpha: 0.1),
                          blurRadius: 4,
                          offset: const Offset(0, 2),
                        ),
                      ],
                    ),
                    padding: const EdgeInsets.all(18.0),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text(
                          'Click Me to Reveal!',
                          style: TextStyle(
                            fontSize: 18.0,
                            fontWeight: FontWeight.w600,
                            color: Colors.deepPurple,
                          ),
                        ),
                        RotationTransition(
                          turns: Tween(
                            begin: 0.0,
                            end: 0.5,
                          ).animate(animation),
                          child: Icon(
                            _myCustomExpansibleController.isExpanded
                                ? Icons.keyboard_arrow_up
                                : Icons.keyboard_arrow_down,
                            color: Colors.deepPurple,
                            size: 28,
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              },
              bodyBuilder: (BuildContext context, Animation<double> animation) {
                return SizeTransition(
                  sizeFactor: animation,
                  axisAlignment: -1.0,
                  child: FadeTransition(
                    opacity: animation,
                    child: Container(
                      margin: const EdgeInsets.only(top: 10.0),
                      padding: const EdgeInsets.all(20.0),
                      decoration: BoxDecoration(
                        color: Colors.deepPurple.shade50,
                        borderRadius: BorderRadius.circular(12.0),
                        border: Border.all(
                          color: Colors.deepPurple.shade100,
                          width: 2,
                        ),
                      ),
                      child: const Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            '🎉 Voila! Here\'s your secret content.',
                            style: TextStyle(
                              fontSize: 16.0,
                              color: Colors.black87,
                            ),
                          ),
                          SizedBox(height: 10.0),
                          Text(
                            'You can put anything here: forms, images, lists, or even other expandable widgets!',
                            style: TextStyle(
                              fontSize: 14.0,
                              color: Colors.grey,
                            ),
                          ),
                          SizedBox(height: 10.0),
                          Row(
                            children: [
                              Icon(Icons.check_circle, color: Colors.green),
                              SizedBox(width: 8),
                              Text('Fully customizable layout'),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                );
              },
              expansibleBuilder:
                  (
                  BuildContext context,
                  Widget header,
                  Widget body,
                  Animation<double> animation,
                  ) {
                return Column(children: [header, body]);
              },
              duration: const Duration(milliseconds: 400),
              curve: Curves.easeInOutQuad,
              reverseCurve: Curves.easeOutCubic,
              maintainState: true, // Keep state even when collapsed
            ),
            const SizedBox(height: 30),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () => _myCustomExpansibleController.expand(),
                  child: const Text('Expand'),
                ),
                ElevatedButton(
                  onPressed: () => _myCustomExpansibleController.collapse(),
                  child: const Text('Collapse'),
                ),
                ElevatedButton(
                  onPressed: () {
                    if (_myCustomExpansibleController.isExpanded) {
                      _myCustomExpansibleController.collapse();
                    } else {
                      _myCustomExpansibleController.expand();
                    }
                  },
                  child: const Text('Toggle'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Conclusion:

In the article, I have explained how the Expansible Widget In Flutter; you can modify this code according to your choice. This was a small introduction to Expansible Widget in Flutter User Interaction from my side, and it’s working using Flutter.

I hope this blog will provide you with sufficient information on trying the Expansible Widget in your Flutter projectsMore than just a back-end update, Flutter’s new Expansible widget provides developers with the means to create more dynamic, personalised, and aesthetically pleasing expandable user interfaces.

So go ahead and start creating the precise user interface your app requires by refactoring that outdated ExpansionTileController and investigating the new Expansible. So please try it.

❤ ❤ Thanks for reading this article ❤❤

If I need to correct something? Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.


From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire Flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on FacebookGitHubTwitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.


Leave comment

Your email address will not be published. Required fields are marked with *.