Debug School

rakesh kumar
rakesh kumar

Posted on

Best practice to defining provider in flutter

Set up your providers at the root level

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => InfluencerViewModel(InfluencerAPI()),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
   return const MaterialApp(
      title: 'Influencer App',
      home: Influencerscreen(),
    );

  }
}
Enter fullscreen mode Exit fullscreen mode

Set up your providers at Specific Widgets

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Influencer App',
      home: InfluencerProvider(),
    );
  }
}

class InfluencerProvider extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => InfluencerViewModel(InfluencerAPI()),
      child: Influencerscreen(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, only the Influencerscreen widget and its descendants have access to the InfluencerViewModel.

Set up ]multiple providers at the root level

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_application_1/Service/Influencer/influencer_service.dart';
import 'package:flutter_application_1/view_modal/influencer_view_model.dart';
import 'package:flutter_application_1/Screen/Influencer/Influencerscreen.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => InfluencerViewModel(InfluencerAPI()),
        ),
        // Add more providers here
        ChangeNotifierProvider(
          create: (_) => AnotherViewModel(),
        ),
        ChangeNotifierProvider(
          create: (_) => YetAnotherViewModel(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Influencer App',
      home: Influencerscreen(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Practical Example

To achieve this, we need to create two separate ViewModels: one for fetching the list of countries and another for fetching the list of tags. We'll then use these ViewModels in the Influencerscreen to populate the country dropdown and the multi-select filter.

Step-by-Step Implementation:
Create ViewModel for Countries:

import 'package:flutter/foundation.dart';
import 'package:flutter_application_1/service/country_service.dart';

class CountryViewModel extends ChangeNotifier {
  final CountryService api;
  List<String> countries = [];
  bool isLoading = false;

  CountryViewModel(this.api);

  Future<void> fetchCountries() async {
    isLoading = true;
    notifyListeners();

    try {
      countries = await api.getCountries();
    } catch (e) {
      print(e);
    } finally {
      isLoading = false;
      notifyListeners();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Create ViewModel for Tags:

import 'package:flutter/foundation.dart';
import 'package:flutter_application_1/service/tag_service.dart';

class TagViewModel extends ChangeNotifier {
  final TagService api;
  List<String> tags = [];
  bool isLoading = false;

  TagViewModel(this.api);

  Future<void> fetchTags() async {
    isLoading = true;
    notifyListeners();

    try {
      tags = await api.getTags();
    } catch (e) {
      print(e);
    } finally {
      isLoading = false;
      notifyListeners();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Modify the Main App to Include the New ViewModels:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_application_1/view_modal/influencer_view_model.dart';
import 'package:flutter_application_1/view_modal/country_view_model.dart';
import 'package:flutter_application_1/view_modal/tag_view_model.dart';
import 'package:flutter_application_1/service/influencer_service.dart';
import 'package:flutter_application_1/service/country_service.dart';
import 'package:flutter_application_1/service/tag_service.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => InfluencerViewModel(InfluencerAPI()),
        ),
        ChangeNotifierProvider(
          create: (_) => CountryViewModel(CountryService()),
        ),
        ChangeNotifierProvider(
          create: (_) => TagViewModel(TagService()),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Influencer App',
      home: Influencerscreen(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Update the Influencerscreen to Use the New ViewModels:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_application_1/view_modal/influencer_view_model.dart';
import 'package:flutter_application_1/view_modal/country_view_model.dart';
import 'package:flutter_application_1/view_modal/tag_view_model.dart';

class Influencerscreen extends StatefulWidget {
  const Influencerscreen({super.key});
  @override
  State<Influencerscreen> createState() => _InfluencerscreenState();
}

class _InfluencerscreenState extends State<Influencerscreen> {
  TextEditingController _searchController = TextEditingController();
  String _selectedCountry = 'All';
  List<String> _selectedTags = [];

  @override
  void initState() {
    super.initState();
    Future.microtask(() {
      Provider.of<InfluencerViewModel>(context, listen: false).fetchInfluencers();
      Provider.of<CountryViewModel>(context, listen: false).fetchCountries();
      Provider.of<TagViewModel>(context, listen: false).fetchTags();
    });
  }

  @override
  Widget build(BuildContext context) {
    final influencerViewModel = Provider.of<InfluencerViewModel>(context);
    final countryViewModel = Provider.of<CountryViewModel>(context);
    final tagViewModel = Provider.of<TagViewModel>(context);

    List<SearchInfluencer> filteredInfluencers = influencerViewModel.influencers.where((influencer) {
      bool matchesSearch = influencer.userName != null &&
          influencer.userName!.toLowerCase().contains(_searchController.text.toLowerCase());
      bool matchesCountry = _selectedCountry == 'All' ||
          (influencer.country != null && influencer.country == _selectedCountry);
      bool matchesTags = _selectedTags.isEmpty ||
          (influencer.tags != null && _selectedTags.every((tag) => influencer.tags!.contains(tag)));
      return matchesSearch && matchesCountry && matchesTags;
    }).toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Influencers'),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              controller: _searchController,
              decoration: InputDecoration(
                labelText: 'Search by name',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                setState(() {});
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: countryViewModel.isLoading
                ? CircularProgressIndicator()
                : DropdownButton<String>(
                    isExpanded: true,
                    value: _selectedCountry,
                    onChanged: (newValue) {
                      setState(() {
                        _selectedCountry = newValue!;
                      });
                    },
                    items: ['All', ...countryViewModel.countries].map((country) {
                      return DropdownMenuItem<String>(
                        value: country,
                        child: Text(country),
                      );
                    }).toList(),
                  ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: tagViewModel.isLoading
                ? CircularProgressIndicator()
                : Wrap(
                    spacing: 8.0,
                    children: tagViewModel.tags.map((tag) {
                      bool isSelected = _selectedTags.contains(tag);
                      return FilterChip(
                        label: Text(tag),
                        selected: isSelected,
                        onSelected: (selected) {
                          setState(() {
                            if (selected) {
                              _selectedTags.add(tag);
                            } else {
                              _selectedTags.remove(tag);
                            }
                          });
                        },
                      );
                    }).toList(),
                  ),
          ),
          Expanded(
            child: influencerViewModel.isLoading
                ? const Center(child: CircularProgressIndicator())
                : filteredInfluencers.isEmpty
                    ? const Center(child: Text('No influencers found'))
                    : ListView.builder(
                        itemCount: filteredInfluencers.length,
                        itemBuilder: (context, index) {
                          final influencer = filteredInfluencers[index];
                          return ListTile(
                            title: Text(influencer.userName ?? "No Name"),
                            subtitle: Text(influencer.slug ?? "No Details"),
                          );
                        },
                      ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => influencerViewModel.fetchInfluencers(),
        child: const Icon(Icons.refresh),
      ),
    );
  }

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)