Does set needed in provider? - flutter

I have provider that I build by learning from course, and there is set variables if I remove it nothing change and everything works fine but I'm afraid in future something will go error.
Here is my code:
class SimilarMovieProvider with ChangeNotifier {
List<SimilarMovieModel> _similarMovie = [];
bool _isLoading = true;
List<SimilarMovieModel> get similarMovie => _similarMovie;
bool get isLoading => _isLoading;
set similarMovie(List<SimilarMovieModel> _similarMovie) {
_similarMovie = similarMovie;
notifyListeners();
}
set isLoading(bool _isLoading) {
_isLoading = isLoading;
notifyListeners();
}
Future getSimilarMovie(movieId) async {
_isLoading = true;
try {
List<SimilarMovieModel> similarMovie =
await Http().getSimilarMovie(movieId);
_similarMovie = similarMovie;
_isLoading = false;
notifyListeners();
} catch (error) {
_isLoading = false;
notifyListeners();
print(error);
}
}
}

As far as I am concerned it doesn't change anything. You just switch from using a setter to using a method to update your instance.

Related

How to navigate to another page after using a provider function in flutter?

I have created a function in my Login provider to verify OTP for my app like below.
Future<bool> verifyOtp(String otp) async {
final _loginData = await SharedPreferences.getInstance();
_isLoading = true;
notifyListeners();
_status = await AuthApi.verifyOtp(otp);
_isLoading = false;
_name = _loginData.getString('name');
notifyListeners();
return _status;
}
Now whenever I am trying to use this on my code like below,
final bool status = await Provider.of<LoginProvider>(context, listen: false).verifyOtp(verificationCode);
// ignore: avoid_print
print("STATUS ==== " + status.toString());
if (status) {
Navigator.of(context).pushReplacementNamed('/discover');
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Incorrect OTP!!!")));
}
It's giving me an exception like below -
Exception has occurred.
FlutterError (This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.)
Anyone please guide me, what is the actual way to navigate from a provider. I am very new in Provider. Thank you so much :)
----- Full Provider Code is Below -----
class LoginProvider with ChangeNotifier {
bool _status = false;
bool _isLoading = false;
bool _isOtpScreen = false;
String? _name;
bool get isLoading => _isLoading;
bool get isOtpScreen => _isOtpScreen;
String? get name => _name;
void sendOtp(String phone) async {
_isLoading = true;
notifyListeners();
_status = await AuthApi.sendOtp(phone);
_isLoading = false;
_isOtpScreen = true;
notifyListeners();
}
Future<bool> verifyOtp(String otp) async {
final _loginData = await SharedPreferences.getInstance();
_isLoading = true;
notifyListeners();
_status = await AuthApi.verifyOtp(otp);
_isLoading = false;
_name = _loginData.getString('name');
notifyListeners();
return _status;
}
}
Use a GlobalKey you can access from anywhere to navigate
Create the key
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
Pass it to your App:
new MaterialApp(
title: 'MyApp',
onGenerateRoute: generateRoute,
navigatorKey: navigatorKey,
);
Use in your route:
print("STATUS ==== " + status.toString());
if (status) {
Navigator.of(navigatorKey.currentContext).pushReplacementNamed('/discover');
} else {
ScaffoldMessenger.of(navigatorKey.currentContext).showSnackBar(const SnackBar(content:
Text("Incorrect OTP!!!")));
}
final bool status = await Provider.of<LoginProvider>(context, listen: false).verifyOtp(verificationCode);
// ignore: avoid_print
print("STATUS ==== " + status.toString());
if (status) {
Navigator.of(context).pushReplacementNamed('/discover');
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Incorrect OTP!!!")));
}
change this code to the next one and i think it will work
but you must add this code in build function if it's stateless widget
final provider = Provider.of<LoginProvider>(context);
final status = await provider.verifyOtp(verificationCode);
// ignore: avoid_print
print("STATUS ==== " + status.toString());
if (status) {
Navigator.of(context).pushReplacementNamed('/discover');
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Incorrect OTP!!!")));
}
this will work fine with you i hope it help you

How do i create a rounded search bar in flutter that also shows the recent searches from the search bar?

I have been wanting to find a solution to create a rounded search bar in flutter which also shows the list of recent searches underneath it. How is it possible to create the previous widget?
Using the package below, you can save the information you want to the device memory and then withdraw it from there (username, password, search history, etc.). The sample code is in the document.
https://pub.dev/packages/shared_preferences
like this:
void handleRememberMe(bool? value) {
_isChecked = value!;
SharedPreferences.getInstance().then(
(prefs) {
prefs.setBool("remember_me", value);
prefs.setString('userName', userNameController.text);
prefs.setString('password', passwordController.text);
},
);
setState(() {
_isChecked = value;
});
}
void loadUserEmailPassword() async {
try {
SharedPreferences _prefs = await SharedPreferences.getInstance();
var _email = _prefs.getString("userName") ?? "";
var password = _prefs.getString("password") ?? "";
var _remeberMe = _prefs.getBool("remember_me") ?? false;
if (_remeberMe) {
setState(() {
_isChecked = true;
});
userNameController.text = _email;
passwordController.text = password;
} else {
userNameController.text = "";
passwordController.text = "";
setState(() {
_isChecked = false;
});
}
} catch (e) {
debugPrint(e.toString());
}
}

Null check operator used on a null value- futter chat

I'm creating a chat app with sending voice message. now I'm getting this error in my code. appreciate your help on this. I HAVE INSERTED FOLLOWING CODE ANS THEN ERROR APPEARS. Cant find a exact file error occurring.
Null check operator used on a null value
class SoundRecorder {
FlutterSoundRecorder? _audioRecorder;
bool _isRecorderInitialised = false;
bool get isRecording => _audioRecorder!.isRecording;
Future init() async {
_audioRecorder = FlutterSoundRecorder();
await _audioRecorder?.openAudioSession(); //start recording
//asking permisson
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw RecordingPermissionException("Microphone permission");
}
await _audioRecorder!.openAudioSession();
_isRecorderInitialised = true;
}
void dispose() {
if (!_isRecorderInitialised) return;
_audioRecorder!.closeAudioSession();
_audioRecorder = null;
_isRecorderInitialised = false;
}
Future _record() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.startRecorder(toFile: pathToSaveAudio);
}
Future _stop() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.stopRecorder();
}
Future toggleRecording() async {
if (_audioRecorder!.isStopped) {
await _record();
} else {
await _stop();
}
}
}
Looks like at some point you use the ! operator to assert that _audioRecorder isn't null but it actually is. From the stack, I think this would be from the isRecording getter.
A simple fix to this would be to make the getter bool get isRecording => _audioRecorder?.isRecording ?? false, since if _audioRecorder is null, then you can't be recording, right?

Display Loading spinner waitint for request to complete while using provider package

I am using a provider package. I want to display a loading spinner while waiting for a request to complete. The pattern below is too verbose. Please help me make it less verbose. Here is my code
class APIService with ChangeNotifier {
// Check for working API backend
bool isWorking = false;
bool isLoading = false;
set _isLoading(bool value) {
isLoading = value; <--
notifyListeners();
}
Future<bool> selectAPI(String input) async {
_isLoading = true; <-- 1
final uri = Uri.tryParse('https://$input$url')!;
final response = await http.get(uri);
if (response.statusCode == 200) {
final body = jsonDecode(response.body) as Map<String, dynamic>;
bool isTrue = body['info']['title'] == 'SamFetch';
_isLoading = false; <-- 2
notifyListeners();
return isWorking = isTrue;
}
_isLoading = false; <-- 3
throw response;
}
}
Here is my UI code
IconButton(
icon: apiService.isLoading
? CircularProgressIndicator()
: Icon(Icons.done),
onPressed: () async {
await addAPI(apiService, cache);
}),
}
Below is addAPI() method
Future<void> addAPI(APIService apiService, Cache cache) async {
if (api != null) {
try {
await apiService.selectAPI(api!);
if (apiService.isWorking) {
await cache.saveAppName(api!);
}
} on SocketException catch (e) {
print(e);
} catch (e) {
await cache.clearCache();
}
}
}
Is setState the final solution?
You can use Future Builder and set your Future Function in future attribute. You can control the visible widget based on the status of your function. So you dont have to use isloading variable.

Correct way to call an api by provider in fflutter?

I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}