Flutter Flows Made Easy!

Dec 15, 2023 | Programming

Welcome to this guide on simplifying flow management in Flutter applications using the Flow Builder library. This powerful tool allows developers to create user-friendly navigation stacks driven by state changes. Let’s dive into how to define a flow state, create a FlowBuilder, update the flow state, and complete the flow.

Define a Flow State

The flow state is crucial as it drives the flow of your application. Whenever this state changes, a new navigation stack is generated based on the updated flow state. Think of it as building a house where each room represents a different state of your application.

dart
class Profile {
  const Profile(this.name, this.age, this.weight);
  final String? name;
  final int? age;
  final int? weight;

  Profile copyWith(String? name, int? age, int? weight) {
    return Profile(
      name: name ?? this.name,
      age: age ?? this.age,
      weight: weight ?? this.weight,
    );
  }
}

Create a Flow Builder

A FlowBuilder is a widget that creates a navigation stack in response to changes in the flow state. Every time the state shifts, the onGeneratePages method is invoked to return a new list of pages based on the current state. Here’s how it works:

dart
FlowBuilder(
  state: const Profile(),
  onGeneratePages: (profile, pages) {
    return [
      MaterialPage(child: NameForm()),
      if (profile.name != null) MaterialPage(child: AgeForm()),
    ];
  },
);

Update the Flow State

You can update the state of the flow with ease via the context. When a user provides new information, simply use the built-in methods to trigger state updates:

dart
class NameForm extends StatefulWidget {
  @override
  _NameFormState createState() => _NameFormState();
}

class _NameFormState extends State {
  var _name = '';

  void _continuePressed() {
    context.flow().update((profile) => profile.copyWith(name: _name));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Name')),
      body: Center(
        child: Column(
          children: [
            TextField(
              onChanged: (value) => setState(() => _name = value),
              decoration: InputDecoration(
                labelText: 'Name',
                hintText: 'John Doe',
              ),
            ),
            RaisedButton(
              child: const Text('Continue'),
              onPressed: _name.isNotEmpty ? _continuePressed : null,
            )
          ],
        ),
      ),
    );
  }
}

Complete the Flow

Completing the flow can be achieved in a similar fashion. Use the context to complete the current flow state with the required data:

dart
class AgeForm extends StatefulWidget {
  @override
  _AgeFormState createState() => _AgeFormState();
}

class _AgeFormState extends State {
  int? _age;

  void _continuePressed() {
    context
        .flow()
        .complete((profile) => profile.copyWith(age: _age));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Age')),
      body: Center(
        child: Column(
          children: [
            TextField(
              onChanged: (value) => setState(() => _age = int.parse(value)),
              decoration: InputDecoration(
                labelText: 'Age',
                hintText: '42',
              ),
              keyboardType: TextInputType.number,
            ),
            RaisedButton(
              child: const Text('Continue'),
              onPressed: _age != null ? _continuePressed : null,
            )
          ],
        ),
      ),
    );
  }
}

Using Flow Controller

If you need to manipulate the flow from outside of the sub-tree, you can create a custom FlowController:

dart
class MyFlow extends StatefulWidget {
  @override
  State createState() => _MyFlowState();
}

class _MyFlowState extends State {
  late FlowController _controller;

  @override
  void initState() {
    super.initState();
    _controller = FlowController(const Profile());
  }

  @override
  Widget build(BuildContext context) {
    return FlowBuilder(
      controller: _controller,
      onGeneratePages: ...
    );
  }

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

Troubleshooting

If you encounter issues with your FlowBuilder or have questions about navigating states, consider the following troubleshooting steps:

  • Ensure that your Profile state is correctly set up and is being passed accurately at each state change.
  • If the navigation is not functioning as expected, check the conditions in your onGeneratePages method to verify that the logic flows correctly.
  • Make sure there are no mismatches in the expected data types, especially when updating the flow state.

For more insights, updates, or to collaborate on AI development projects, stay connected with **fxis.ai**.

At **fxis.ai**, we believe that such advancements are crucial for the future of AI, as they enable more comprehensive and effective solutions. Our team is continually exploring new methodologies to push the envelope in artificial intelligence, ensuring that our clients benefit from the latest technological innovations.

Stay Informed with the Newest F(x) Insights and Blogs

Tech News and Blog Highlights, Straight to Your Inbox