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(),
);
}
}
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(),
);
}
}
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(),
);
}
}
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();
}
}
}
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();
}
}
}
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(),
);
}
}
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();
}
}
Top comments (0)