Flutter State Management

Hamish Friedlander | RUSH HoE headshot

Hamish Friedlander

Head of Engineering

May 30, 2024

6 mins

Hamish Friedlander presenting Flutter State Management

A Guide for React Developers

Flutter and React, two popular frameworks for building user interfaces, share many similarities in terms of component-based architecture and state management. However, transitioning from React to Flutter can be challenging, especially when it comes to handling state. In this blog post, we'll explore state management techniques in Flutter, drawing parallels to React concepts, and provide code snippets to illustrate the implementation.

React State and Props

In React, state represents the internal data of a component, while props are used to pass data from parent components to child components. React components can be classified as class components or functional components. Class components have their own state and lifecycle methods, while functional components rely on hooks for state management and side effects.

// React class component

class Counter extends React.Component {

  state = { count: 0 };

  increment = () => {

    this.setState({ count: this.state.count + 1 });

  };

  render() {

    return (

      <div>

        <p>Count: {this.state.count}</p>

        <button onClick={this.increment}>Increment</button>

      </div>

    );

  }

}

// React functional component with hooks

const Counter = () => {

  const [count, setCount] = useState(0);

  const increment = () => {

    setCount(count + 1);

  };

  return (

    <div>

      <p>Count: {count}</p>

      <button onClick={increment}>Increment</button>

    </div>

  );

};

Flutter Widgets

Flutter uses a widget-based approach to build user interfaces. Widgets are the building blocks of Flutter apps and can be classified as Stateless or Stateful. Stateless widgets are immutable and rely on external data, similar to React's stateless components. Stateful widgets, on the other hand, have their own mutable state, analogous to React's class components.

// Flutter Stateless widget

class Greeting extends StatelessWidget {

  final String name;

  const Greeting({required this.name});

  @override

  Widget build(BuildContext context) {

    return Text('Hello, $name!');

  }

}

// Flutter Stateful widget

class Counter extends StatefulWidget {

  @override

  _CounterState createState() => _CounterState();

}

class _CounterState extends State<Counter> {

  int count = 0;

  void increment() {

    setState(() {

      count++;

    });

  }

  @override

  Widget build(BuildContext context) {

    return Column(

      children: [

        Text('Count: $count'),

        ElevatedButton(

          onPressed: increment,

          child: Text('Increment'),

        ),

      ],

    );

  }

}

State Management Solutions

As Flutter apps grow in complexity, managing state across widgets becomes crucial. Flutter offers various state management solutions, including Provider, Riverpod, and BLoC (Business Logic Component). In this post, we'll focus on Riverpod, a powerful and flexible state management library.

Riverpod 2.0

Riverpod is a state management library that simplifies dependency injection and state sharing in Flutter. It provides a set of tools to manage state, handle asynchronous operations, and ensure a unidirectional flow of data.

// Defining a provider

final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {

  return CounterNotifier();

});

// Defining a notifier

class CounterNotifier extends StateNotifier<int> {

  CounterNotifier() : super(0);

  void increment() {

    state++;

  }

}

// Using the provider in a widget

class CounterWidget extends ConsumerWidget {

  @override

  Widget build(BuildContext context, WidgetRef ref) {

    final count = ref.watch(counterProvider);

    return Column(

      children: [

        Text('Count: $count'),

        ElevatedButton(

          onPressed: () => ref.read(counterProvider.notifier).increment(),

          child: Text('Increment'),

        ),

      ],

    );

  }

}

Handling Asynchronous Data

Fetching and managing asynchronous data is a common scenario in Flutter apps. Riverpod provides AsyncNotifierProvider to handle asynchronous operations and manage the loading state.

// Defining an async provider

final postsProvider = AsyncNotifierProvider<PostsNotifier, List<Post>>(() {

  return PostsNotifier();

});

// Defining an async notifier

class PostsNotifier extends AsyncNotifier<List<Post>> {

  @override

  Future<List<Post>> build() async {

    return fetchPosts();

  }

  Future<List<Post>> fetchPosts() async {

    // Simulating an API call

    await Future.delayed(Duration(seconds: 2));

    return [

      Post(id: 1, title: 'Post 1'),

      Post(id: 2, title: 'Post 2'),

    ];

  }

}

// Using the async provider in a widget

class PostsWidget extends ConsumerWidget {

  @override

  Widget build(BuildContext context, WidgetRef ref) {

    final postsAsync = ref.watch(postsProvider);

    return postsAsync.when(

      loading: () => CircularProgressIndicator(),

      error: (error, stack) => Text('Error: $error'),

      data: (posts) => ListView.builder(

        itemCount: posts.length,

        itemBuilder: (context, index) {

          final post = posts[index];

          return ListTile(

            title: Text(post.title),

          );

        },

      ),

    );

  }

}

Conclusion

Flutter provides a rich set of tools and libraries for state management, making it easy for React developers to transition to Flutter development. By understanding the similarities and differences between React and Flutter concepts, developers can leverage their existing knowledge to build robust and scalable Flutter apps.

Riverpod, with its simplicity and flexibility, has become a popular choice for state management in Flutter. It offers a clear and concise way to manage state, handle asynchronous operations, and ensure a unidirectional flow of data.

Whether you're a seasoned React developer or just starting with Flutter, exploring state management techniques and libraries like Riverpod will empower you to build efficient and maintainable Flutter applications.

Happy coding!

Arrow right