Introduction

In the world of Flutter app development, efficient state management is a key factor in creating robust and responsive applications. Two powerful packages, Flutter Provider and Injector, provide excellent solutions for managing state and dependencies. In this tutorial, we will explore how to combine Flutter Provider and Injector to achieve efficient state management in the context of a weather app. We will build a code example that fetches weather data from a network, stores it in model classes, creates a model view, and displays the data in the UI. Let’s dive in!

Understanding Flutter Provider and Injector

Before we dive into the code example, let’s briefly understand Flutter Provider and Injector.

Flutter Provider: Flutter Provider is a popular state management package that facilitates the sharing and observation of state across different parts of a Flutter application. It follows the InheritedWidget pattern and simplifies the process of updating and accessing state.

Injector: Injector is a powerful dependency injection package for Flutter. It provides a simple and efficient way to manage dependencies and enables the injection of objects into widgets. By using Injector, you can easily access instances of objects throughout your app without manually passing them around.

Setting Up Provider and Injector in Flutter

To get started, let’s set up Flutter Provider and Injector in our Flutter project. Follow the steps below:

  1. Create a new Flutter project or open an existing one.
  2. Open your pubspec.yaml file and add the following dependencies:
dependencies:
  provider: ^5.0.0
  injector: ^1.0.0
  http: ^0.13.0
  ...
  1. Save the file, and run flutter pub get in the terminal to fetch the dependencies.
  2. Once the dependencies are downloaded, we can start using Flutter Provider and Injector in our project.

Creating a Weather App Code Example: Flutter Provider + Injector

In this code example, we will create a weather app that fetches weather data from a network API, stores it in model classes, creates a model view, and displays the data in the UI. This example demonstrates how to combine Flutter Provider and Injector effectively.

1. Creating the Model Classes

First, let’s define the model classes that represent the weather data. Create a new file called weather_model.dart and add the following code:

class WeatherModel {
  final String city;
  final double temperature;
  final String condition;

  WeatherModel({
    required this.city,
    required this.temperature,
    required this.condition,
  });
}

2. Fetching Weather Data

Next, let’s create a weather service that fetches the weather data from a network API. Create a new file called weather_service.dart and add the following code:

import 'dart:convert';
import 'package:http/http.dart' as http;

class WeatherService {
  Future<WeatherModel> fetchWeatherData() async {
    final response = await http.get(Uri.parse('https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=London'));

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final location = data['location']['name'];
      final temp = data['current']['temp_c'];
      final condition = data['current']['condition']['text'];

      return WeatherModel(
        city: location,
        temperature: temp,
        condition: condition,
      );
    } else {
      throw Exception('Failed to fetch weather data');
    }
  }
}

Make sure to replace YOUR_API_KEY with your actual API key for the weather service.

3. Creating the Model View

Now, let’s create a model view that handles the state and business logic for the weather app. Create a new file called weather_model_view.dart and add the following code:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:injector/injector.dart';
import 'weather_model.dart';
import 'weather_service.dart';

class WeatherModelView extends ChangeNotifier {
  final WeatherService _weatherService;

  WeatherModelView(this._weatherService);

  WeatherModel? _weatherData;
  WeatherModel? get weatherData => _weatherData;

  Future<void> fetchWeatherData() async {
    try {
      final weatherData = await _weatherService.fetchWeatherData();
      _weatherData = weatherData;
      notifyListeners();
    } catch (e) {
      print(e);
    }
  }
}

4. Creating the UI

Finally, let’s create the UI to display the weather data. In the main.dart file, add the following code:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:injector/injector.dart';
import 'weather_model_view.dart';

void main() {

final injector = Injector.appInstance; 
injector.registerSingleton<WeatherService>(() => WeatherService()); 
injector.registerSingleton<WeatherModelView>( () => WeatherModelView(injector.get<WeatherService>()), ); 
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => Injector.appInstance.get<WeatherModelView>(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final weatherModelView = Provider.of<WeatherModelView>(context);

    return MaterialApp(
      title: 'Flutter Provider + Injector Weather App',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Weather App'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RaisedButton(
                onPressed: weatherModelView.fetchWeatherData,
                child: Text('Fetch Weather Data'),
              ),
              SizedBox(height: 16),
              Consumer<WeatherModelView>(
                builder: (_, modelView, __) {
                  final weatherData = modelView.weatherData;

                  if (weatherData != null) {
                    return Column(
                      children: [
                        Text(
                          'City: ${weatherData.city}',
                          style: TextStyle(fontSize: 24),
                        ),
                        SizedBox(height: 8),
                        Text(
                          'Temperature: ${weatherData.temperature}°C',
                          style: TextStyle(fontSize: 24),
                        ),
                        SizedBox(height: 8),
                        Text(
                          'Condition: ${weatherData.condition}',
                          style: TextStyle(fontSize: 24),
                        ),
                      ],
                    );
                  } else {
                    return Text(
                      'Press the button to fetch weather data.',
                      style: TextStyle(fontSize: 24),
                    );
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

In the code above, we create a WeatherModelView instance using Provider.of and use the Consumer widget to listen for changes in the weather data. When the “Fetch Weather Data” button is pressed, the fetchWeatherData method is called, and the weather data is displayed in the UI.

Best Practices for Using Flutter Provider and Injector

To make the most out of Flutter Provider and Injector, consider the following best practices:

  1. Utilize the Consumer widget when you only need to update a specific part of the UI based on state changes.
  2. Leverage the power of ChangeNotifier and notifyListeners to efficiently update the UI when state changes occur.
  3. Separate your app’s logic into distinct model view classes and inject them using Injector for improved code organization and testability.
  4. Avoid excessive nesting of Provider instances to prevent performance issues. Instead, consider using the Consumer widget and Selector for more targeted state updates.

Conclusion

In this tutorial, we explored how to combine Flutter Provider and Injector for efficient state management in a weather app. We covered the basics of Flutter Provider and Injector, set them up in a Flutter project, and created a code example that demonstrates fetching weather data from a network, storing it in model classes, creating a model view, and displaying the data in the UI. By leveraging the power of Flutter Provider and Injector, you can enhance your app’s state management capabilities and build scalable and maintainable Flutter applications.

If you have any further questions or need additional assistance, please refer to the official documentation of Flutter Provider and Injector.

FAQs

Q: Can I use Flutter Provider and Injector together in any Flutter project? A: Absolutely! Flutter Provider and Injector can be combined seamlessly in any Flutter project to enhance state management and dependency injection capabilities.

Q: Are there any performance considerations when using Flutter Provider and Injector? A: While Flutter Provider and Injector are powerful tools, it’s important to be mindful of performance. Avoid excessive nesting of Provider instances and unnecessary injections to maintain optimal performance. Additionally, consider using the Consumer widget and Selector for more efficient state updates and granular control over UI rendering.