I'm working on a small app with GoogleSignIn-Auth. and stumbled upon a bug I cannot wrap my head around.
It seems like the fold of an Either seems to be skipped. It used to work before, when I had a complicated pile of blocs. Since I started reorganizing my widgets it started to this.
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
signUpSuccess.fold(
(failure) => () {
print("Got failure!");
return Left(GeneralFailure());
},
(success) => () {
return Right(signUpSuccess);
});
print("I skipped the fold!");
} catch (e) {
print("Caught exception!");
return Left(GeneralFailure());
}
print("Instant fail!");
return Left(GeneralFailure());
}
I have a widget that's listening to a SignInBloc emitting the states:
class SignUpRoot extends StatelessWidget {
SignUpRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => sl<SignInBloc>(),
child: BlocListener<SignInBloc, SignInState>(
listener: (context, state) {
if (state is SignInWithGoogleLoaded) {
// Navigate to SignInNamePage
print("This seems to work!");
} else if (state is SignInWithGoogleLoading) {
// Navigate to loading page
print("Loading Google...");
} else if (state is SignInError) {
// Navigate to error page
print("An error occured while signing in!");
}
},
child: const SignUpMainPage(),
)));
}
And last but not least my bloc:
class SignInBloc extends Bloc<SignInEvent, SignInState> {
final SignUpUseCases useCases;
SignInBloc({required this.useCases}) : super(SignInInitial()) {
on<SignInWithGooglePressed>((event, emit) async {
// Show Loading indicator
emit(SignInWithGoogleLoading());
// wait for sign in response
Either<Failure, SignUpSuccess> successOrFailure =
await useCases.signInWithGoogle();
// emit corresponding state
successOrFailure.fold(
(failure) => emit(SignInError()),
(success) => () {
// emit sign in loaded state
emit(SignInWithGoogleLoaded());
// create new (local) user
// assign user data e.g. display name
});
});
}
}
Thanks for any help!
The problem is that the fold method returns the value of the left or right functions.
https://pub.dev/documentation/dartz/latest/dartz/Either/fold.html
B fold<B>(
B ifLeft(
L l
),
B ifRight(
R r
)
)
Your code should be corrected to:
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
return signUpSuccess.fold(
(failure) => () {
return Left(GeneralFailure());
},
(success) => () {
return Right(signUpSuccess);
});
} catch (e) {
return Left(GeneralFailure());
}
return Left(GeneralFailure());
}
I just added the return at the start of the fold, now the value returned from left or right will be returned by your function.
You just have to remove the arrow in the success part of the fold.
Future<Either<Failure, SignUpSuccess>> signInWithGoogle() async {
try {
final signUpSuccess = await googleRemoteDataSource.signInWithGoogle();
signUpSuccess.fold(
(failure) => () {
print("Got failure!");
return Left(GeneralFailure());
},
(success){
return Right(signUpSuccess);
});
print("I skipped the fold!");
} catch (e) {
print("Caught exception!");
return Left(GeneralFailure());
}
print("Instant fail!");
return Left(GeneralFailure());
}
class SignInBloc extends Bloc<SignInEvent, SignInState> {
final SignUpUseCases useCases;
SignInBloc({required this.useCases}) : super(SignInInitial()) {
on<SignInWithGooglePressed>((event, emit) async {
// Show Loading indicator
emit(SignInWithGoogleLoading());
// wait for sign in response
Either<Failure, SignUpSuccess> successOrFailure =
await useCases.signInWithGoogle();
// emit corresponding state
successOrFailure.fold(
(failure) => emit(SignInError()),
(success) {
emit(SignInWithGoogleLoaded());
});
});
}
}
Please i need some help and thanks in advance.
I am reciving over Websocket a Json that i want to safe locally.
But before saving it, i am trying to compare the recieved Json with the existend locad Json. If the value of count_json does not exist inside the local file it would need to save the Json to a new line and if the value of count_json would already exist it would do nothing.
At this moment, i am able to save it localy, and write it down to a new line in the file.
But i have two problems that i do not know how to solve it.
How i am making the comparising is not good. Because it is saving the recieved Json to a new line even the value of count_jsonalready exist, like as follow.
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":1,"range_json":[9.5,8.3,12.4,13.1,8.5],"force_json":[4.9,4.8,4.8,4.9,5]}
{"count_json":1,"range_json":[11.7,9.7,9.9,11.8,10.2],"force_json":[4.9,5,5.2,5.3,5.5]}
{"count_json":2,"range_json":[19.6,19.6,19.6,19.6,19.6],"force_json":[10,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
But i am expenting this
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
My approach on how to compara and safe is as follow.
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
If i open the local Json file in Visual Studio code it give me the error message
End of file Expected
That means the stuctur of how i a writing and saving to the Json file is not properly. See above how the structure is inside the file.
Follow the complete code.
//https://docs.flutter.dev/cookbook/persistence/reading-writing-files
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:web_socket_channel/io.dart';
import 'dart:io';
import 'dart:async';
const String fileName = 'myJsonFile.json';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_HomePageState createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
late IOWebSocketChannel channel;
late bool
connectedS1Status; //boolean value to track if WebSocket is connected
late int istcycles; //variable for istcycles
late double istforcesensor;
late double istrangesensor;
#override
void initState() {
connectedS1Status =
false; //initially connection status is "NO" so its FALSE
Future.delayed(Duration.zero, () async {
channelconnect(); //connect to WebSocket wth NodeMCU
});
// Instantiate _controllerKey and _controllerValue
print('0. Initialized _myjson: $_myjson');
_readJson();
istcycles = 0; //initial value of istcycles
istforcesensor = 0;
istrangesensor = 0;
super.initState();
}
channelconnect() {
try {
channel = IOWebSocketChannel.connect(
"ws://192.168.1.100:80"); //channel IP : Port
channel.stream.listen(
(message) {
//print(message);
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
},
onDone: () {
print("Web socket is closed");
setState(() {
connectedS1Status = false;
});
},
onError: (error) {
print(error.toString());
},
);
} catch (_) {
print("error on connecting to websocket.");
}
}
bool _fileExists = false;
late File _filePath;
// First initialization of _json (if there is no json in the file)
late Map<String, dynamic> _myjson = {};
late String _myjsonString;
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/$fileName');
}
//------------------------------------------------------------------------------------
// _readJson--------------------------------------------------------------------------
//------------------------------------------------------------------------------------
void _readJson() async {
// Initialize _filePath
_filePath = await _localFile;
// 0. Check whether the _file exists
_fileExists = await _filePath.exists();
print('0. File exists? $_fileExists');
// If the _file exists->read it: update initialized _json by what's in the _file
if (_fileExists) {
try {
//1. Read _jsonString<String> from the _file.
_myjsonString = await _filePath.readAsString();
print('1.(_readJson) _jsonString: $_myjsonString');
//2. Update initialized _json by converting _jsonString<String>->_json<Map>
_myjson = jsonDecode(_myjsonString);
print('2.(_readJson) _json: $_myjson \n - \n');
} catch (e) {
// Print exception errors
print('Tried reading _file error: $e');
// If encountering an error, return null
}
}
}
#override
void dispose() {
super.dispose();
}
// Delete Function-------------------------------------------
Future<int> deleteFile() async {
try {
final file = await _localFile;
await file.delete();
} catch (e) {
return 0;
}
return 0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("WebSocket Json"),
backgroundColor: Colors.redAccent),
body: Container(
alignment: Alignment.topCenter, //inner widget alignment to center
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
//showing if websocket is connected or disconnected
child: connectedS1Status
? const Text("WEBSOCKET: CONNECTED")
: const Text("DISCONNECTED")),
Text("Cycles: $istcycles "),
],
)),
);
}
}
Hi I am building flutter function which able to capture qr code and after display the result after user scan QR Code. I need user to be able navigate to previous scanning screen by using button if they need rescan or scan new qr code. This is code for button which on Scanview class.
SizedBox(height: 40,),
CupertinoButton(
color: Color(0xFF88070B),
child:Text("Re-scan QR "),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => **ScanFunction**(),
),
);
}
)
I want to call this _scanQR method from other class ScanFunction . What is proper way when user tap Re-scan QR button and call _scanQR method which is on other class? How to access method from other class? Thanks for help.
class ScanFunction extends StatefulWidget {
#override
ScanFunctionState createState() {
return ScanFunctionState();
}
}
class ScanFunctionState extends State<ScanFunction> {
String result = "Maklumat Inventori";
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scanresultview(qrResult),
),
);
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
result = "Kebenaran kamera telah ditolak";
});
} else {
setState(() {
result = "Ralat tidak diketahui$ex";
});
}
} on FormatException {
setState(() {
result = "Anda menekan butang belakang sebelum mengimbas apa-apa";
});
} catch (ex) {
setState(() {
result = "Ralat tidak diketahui $ex";
});
}
}
You can call this method from initState.
class ScanFunction extends StatefulWidget {
#override
ScanFunctionState createState() {
return ScanFunctionState();
}
}
class ScanFunctionState extends State<ScanFunction> {
#override
void initState() {
_scanQR();
super.initState();
}
String result = "Maklumat Inventori";
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scanresultview(qrResult),
),
);
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
result = "Kebenaran kamera telah ditolak";
});
} else {
setState(() {
result = "Ralat tidak diketahui$ex";
});
}
} on FormatException {
setState(() {
result = "Anda menekan butang belakang sebelum mengimbas apa-apa";
});
} catch (ex) {
setState(() {
result = "Ralat tidak diketahui $ex";
});
}
}
I am using BarcodeScanner package. I get the result after scan the QR code. My question is how to use that result then open the website. Here is my code:
Future scan() async{
Completer<WebViewController> _controller = Completer<WebViewController>();
try {
String barcode = await BarcodeScanner.scan();
setState(() {
this.barcode = barcode;
print(this.barcode);
WebView(
initialUrl: this.barcode,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
});
} on PlatformException catch (e) {
if (e.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
this.barcode = 'The user did not grant the camera permission!';
});
} else {
setState(() => this.barcode = 'Unknown error: $e');
}
} on FormatException{
setState(() => this.barcode = 'null (User returned using the "back"-button before scanning anything. Result)');
} catch (e) {
setState(() => this.barcode = 'Unknown error: $e');
}
}
}
If you use Webview here, after you receive QR code from scanner you need you navigate to new screen. Like this
Navigator.push(
context,
MaterialPageRoute(builder: (context) => WebView(initUrl: data[index].homeLink))
);
Otherwise, you can use url_launcher plugin
https://pub.dev/packages/url_launcher
controller.pauseCamera();
if (await canLaunch(scanData.code)) {
await launch(scanData.code);
}
controller.resumeCamera();
For more details, see here
I have a basic login form, with my LoginModel.
But I do not understand how I can call to the function notifyListeners to display a dialog in my view.
The login widget:
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new ScopedModel<LoginModel>(
model: _loginModel,
child: Center(child: ScopedModelDescendant<LoginModel>(
builder: (context, child, model) {
if (model.status == Status.LOADING) {
return Loading();
}
else return showForm(context);
}))));
}
And the login model:
class LoginModel extends Model {
Status _status = Status.READY;
Status get status => _status;
void onLogin(String username, String password) async {
_status = Status.LOADING;
notifyListeners();
try {
await api.login();
_status = Status.SUCCESS;
notifyListeners();
} catch (response) {
_status = Status.ERROR;
notifyListeners();
}
}
I need to display a dialog when the status is Error
Finally I got this, just returning a Future in the method onLogin
Future<bool> onLogin(String username, String password) async {
_status = Status.LOADING;
notifyListeners();
try {
await api.login();
_status = Status.SUCCESS;
notifyListeners();
return true;
} catch (response) {
_status = Status.ERROR;
notifyListeners();
return false;
}
}
And in the widget:
onPressed: () async {
bool success = await _loginModel.onLogin(_usernameController.text, _passwordController.text);
if(success) {
Navigator.pop(context, true);
}
else{
_showDialogError();
}
}