Flutter webview is blank on IOS but working on android - ios

I'm using flutter webview to present the payment url in my app using the following class:
class YourWebView extends StatelessWidget {
String url;
bool isFinshed = false;
YourWebView(this.url);
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('اكمال عملية الدفع..'),
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () {
if(isFinshed) {
Provider.of<MatchProvider>(context, listen: false)
.getMyComingMatches();
Navigator.of(context).popUntil((route) => route.isFirst);
} else {
Navigator.pop(context);
}
}),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: Uri.encodeFull(url),
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
debuggingEnabled: true,
onPageFinished: (String url) {
SystemChannels.textInput.invokeMethod('TextInput.hide');
if (url.contains("tap/check?tap_id")) {
isFinshed = true;
}
print('Page finished loading: $url');
},
gestureRecognizers: null,
// gestureNavigationEnabled: false
);
}));
}
The url looks like:
https://xxxxxx.com/tap/check?tap_id=chg_TS05162021120xxxxxxx
Everything is working on Android, but on IOS i get a blank screen and i see this error in xcode debug logs :
WebPageProxy::didFailProvisionalLoadForFrame
I have tried to run another urls on the webview and it was working, but the payment url isn't, even though it's working on Android or other browsers.

I think you maybe tried to load a http link in webview instead of https. In that case you should add the following in your info.plist file.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>

I am not familiar with Flutter, but on iOS, if the system's process for rendering the web content terminated, the webview would be blank, the flutter plugin is handling the situation in here, you may have to check the error and do a reload.

Related

flutter web open url and get data

How to open url and get data.
For example I open an url on web browser and after processing I need to fetch the url.
Url launcher lib only return boolean and I can't get data from html lib.
This is the code:
void main() {
runApp(MaterialApp(
home: new Scaffold(
body: new Center(
child: new ElevatedButton(
onPressed: () {
//_launchInBrowser(Uri(scheme: 'https', host: "dart.dev"));
var data = html.window.open("https://dart.dev", "dart dev");
print(data.location);
},
child: new Text('Show Flutter homepage'),
),
),
),
));
}
Future<void> _launchInBrowser(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}
I've found this solution or workaround.
In the web directory create an html file and put a code like that:
<script>
var data = window.open("http://dart.dev");
//alert(window.location.href);
localStorage.setItem("url", window.location.href);
console.log(localStorage.getItem("url"));
</script>
I need the url but it is possible to store any data from the page.
From flutter use the local storage:
import 'dart:html' as html;
class LocalStorage {
final html.Storage _localStorage = html.window.localStorage;
Future save(String url) async {
_localStorage['url'] = url;
}
Future<String?> getUrl() async => _localStorage['url'];
Future invalidate() async {
_localStorage.remove('url');
}
}
I tried with shared_preferences but it doesn't work.
Now with the url_launcher lib:
ElevatedButton(
onPressed: () async {
var rep = LocalStorage();
await _launchInBrowser(Uri(path: "open_url.html"));
print(await rep.getUrl());
},
child: Text('Show Flutter homepage'),
),
Future<void> _launchInBrowser(Uri url) async {
if (!await launchUrl(
url,
mode: LaunchMode.externalApplication,
)) {
throw 'Could not launch $url';
}
}

flutter_facebook_login plugin issues logging in on iOS simulator (using Android Studio)

I'm building a Flutter app with Android Studio (a Time Tracker, following a course on Udemy) and I am at the stage where I have created a sign-in page, that allows me to sign in using either Google, Facebook, email or 'going anonymous'. I'm using version 2.0.1 of the flutter_facebook_login plugin, since the latest version, version 3.0.0, generates lots of errors related to Cocoapods. Version 2.0.1 resolves all of those errors.
I'm doing all authentication using Flutter's firebase_auth package, so that a unique user ID can be generated, to control what is seen by each user. The sign-in process is split into two different pages. There's an 'auth.dart' page that handles all of the authorisation work, with Firebase, Google and Facebook etc. It looks like this:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:google_sign_in/google_sign_in.dart';
class User {
User({#required this.uid});
final String uid;
}
abstract class AuthBase {
Stream<User> get onAuthStateChanged;
Future<User> currentUser();
Future<User> signInAnonymously();
Future<User> signInWithGoogle();
Future<User> signInWithFacebook();
Future<void> signOut();
}
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
User _userFromFirebase(FirebaseUser user) {
if (user == null) {
return null;
}
return User(uid: user.uid);
}
#override
Stream<User> get onAuthStateChanged {
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
#override
Future<User> currentUser() async {
final user = await _firebaseAuth.currentUser();
return _userFromFirebase(user);
}
#override
Future<User> signInAnonymously() async {
final authResult = await _firebaseAuth.signInAnonymously();
return _userFromFirebase(authResult.user);
}
#override
Future<User> signInWithGoogle() async {
final googleSignIn = GoogleSignIn();
final googleAccount = await googleSignIn.signIn();
if (googleAccount != null) {
final googleAuth = await googleAccount.authentication;
if (googleAuth.accessToken != null && googleAuth.idToken != null) {
final authResult = await _firebaseAuth.signInWithCredential(
GoogleAuthProvider.getCredential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
),
);
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_MISSING_GOOGLE_AUTH_TOKEN',
message: 'Missing Google Auth Token',
);
}
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}
}
#override
Future<User> signInWithFacebook() async {
final facebookLogin = FacebookLogin();
final result = await facebookLogin.logInWithReadPermissions(
['public_profile'],
);
if (result.accessToken != null) {
final authResult = await _firebaseAuth
.signInWithCredential(FacebookAuthProvider.getCredential(
accessToken: result.accessToken.token,
));
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}
}
#override
Future<void> signOut() async {
final googleSignIn = GoogleSignIn();
await googleSignIn.signOut();
final facebookLogin = FacebookLogin();
await facebookLogin.logOut();
await _firebaseAuth.signOut();
}
}
Then, the sign-in page, with all of the buttons and interactions with Google and Facebook etc. looks like this:
import 'package:flutter/material.dart';
import 'package:time_tracker_flutter_course/app/sign_in/sign_in_button.dart';
import 'package:time_tracker_flutter_course/app/sign_in/social_sign_in_button.dart';
import 'package:time_tracker_flutter_course/services/auth.dart';
class SignInPage extends StatelessWidget {
SignInPage({#required this.auth});
final AuthBase auth;
Future<void> _signInAnonymously() async {
try {
await auth.signInAnonymously();
} catch (e) {
print(e.toString());
}
}
Future<void> _signInWithGoogle() async {
try {
await auth.signInWithGoogle();
} catch (e) {
print(e.toString());
}
}
Future<void> _signInWithFacebook() async {
try {
await auth.signInWithFacebook();
} catch (e) {
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Time Tracker'),
elevation: 2.0,
),
body: _buildContent(),
backgroundColor: Colors.grey[200],
);
}
Widget _buildContent() {
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'Sign In',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 48.0),
SocialSignInButton(
assetName: 'images/google-logo.png',
text: 'Sign in with Google',
textColor: Colors.black87,
color: Colors.white,
onPressed: _signInWithGoogle,
),
SizedBox(height: 8.0),
SocialSignInButton(
assetName: 'images/facebook-logo.png',
text: 'Sign in with Facebook',
textColor: Colors.white,
color: Color(0xFF334D92),
onPressed: _signInWithFacebook,
),
SizedBox(height: 8.0),
SignInButton(
text: 'Sign in with email',
textColor: Colors.white,
color: Colors.teal[700],
onPressed: () {},
),
SizedBox(height: 8.0),
Text(
'or',
style: TextStyle(fontSize: 14.0, color: Colors.black87),
textAlign: TextAlign.center,
),
SizedBox(height: 8.0),
SignInButton(
text: 'Go anonymous',
textColor: Colors.black,
color: Colors.lime[300],
onPressed: _signInAnonymously,
),
],
),
);
}
}
All this code and methodology works perfectly in most cases, which includes:
Android simulator with anonymous login, Google AND Facebook
iOS simulator with anonymous login and Google ONLY
When I try and log in with the Facebook method on the iOS simulator in Android Studio, that's where I run into problems. In the Android Studio console, an error is 'spat out':
flutter: PlatformException(ERROR_ABORTED_BY_USER, Sign in aborted by user, null)
You'll see from the first block of code (the 'auth.dart' code) that this error is just a generic one that I have built in - I haven't been specific with it at all.
I don't believe the issue is with the flutter_facebook_login plugin, since it still works for Android, unless the plug-in has problems that are unique to iOS. I think there's an issue with the iOS set-up for Facebook, even though I have followed the instructions to the letter, including with Xcode.
Can someone help me to understand what might be causing this error, and how I can sort it? It is the only thing in the set-up that you can see that isn't working at the moment, across both simulator platforms.
I had the same issue, I think it is an issue of facebook api with ios beta version.
I found a work around. This is only a work around not the actual solution. It works for me and I hope this helps you:-
The work around checking when the status goes to FacebookLoginStatus.cancelledByUser, then using the below
facebookLogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
It will force flutter to open facebook auth in webview and then you can get it working.
Have a look at the full method
Future signInWithFaceBook() async{
var facebookLogin = new FacebookLogin();
var result = await facebookLogin.logInWithReadPermissions(['email', 'public_profile']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
print(result.accessToken.token);
// Add your route to home page here after sign In
break;
case FacebookLoginStatus.cancelledByUser:
// In your case the program flow will go here as it as a bug with the api I suppose
facebookLogin.loginBehavior = FacebookLoginBehavior.webViewOnly;
// Once the code comes here the above line will force flutter to open facebook auth in a webview
result = await facebookLogin.logInWithReadPermissions(['email', 'public_profile']);
if(result.status==FacebookLoginStatus.loggedIn){
FirebaseUser user = (await _auth.signInWithCredential(FacebookAuthProvider.getCredential(accessToken: result.accessToken.token)
)
).user;
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
// Add your home page here
}
print('CANCELED BY USER');
break;
case FacebookLoginStatus.error:
print(result.errorMessage);
break;
}
}
Update your auth.dart Code with one line of code from the below .It works.
#override
Future <User> signInWithFacebook() async {
final facebookLogin = FacebookLogin();
facebookLogin.LoginBehavior = FacebookLoginBehavior.webViewOnly;
final result = await facebookLogin.logInWithReadPermissions(['public_profile'],);
if (result.accessToken != null) {
final authResult = await _firebaseAuth
.signInWithCredential(FacebookAuthProvider.getCredential(
accessToken: result.accessToken.token,
)
);
return _userFromFirebase(authResult.user);
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: 'Sign in aborted by user',
);
}}

Flutter List + Pull to load more data is not waiting for data to load before it finishes causing the list / scrolling to become unstable

If you make a new Flutter project and include the dependencies and then replace your main.dart file you should be where I am on this question.
I left the original load: with Future.delayed but it doesn't seem to matter. I know partially what my problem is but am unable to come up with a better solution.
1) I don't seem to be using my snapshot.data and instead I am just making a empty List with str and then i just addAll into it and use that. So i'd love to not do that, i originally was using snapshot.data but ran into problems when I tried to "pull to load more data" which happens after you scroll to the bottom of the list.
The problem with my current method of doing this is that if you pull to load more users and then try to pull again before the users have loaded, The app breaks and doesn't wait for the data to properly load. I believe that I need to be doing that all in the load: of this library easy_refresh... but I am not sure how to rewrite my code to accomplish that.
How can I get my data to load with snapshot.data and then when I pull to refresh, I append 100 more users to that list but the UI waits for the list to update before it finishes the load. Would I be better off just putting a Blocking UI element and after the str list updates? and when new users are loaded I unblock the UI? which sorta feels hackish and not the correct way to solve this. The plugin itself should be able to do the loading and when its ready it stops the spinner under the list and says "finished".
pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_easyrefresh: ^1.2.7
http: ^0.12.0+2
main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
backgroundColor: Colors.white
),
home: DuelLeaderBoards(),
);
}
}
class DuelLeaderBoards extends StatefulWidget {
#override
_DuelLeaderBoardsState createState() => _DuelLeaderBoardsState();
}
class _DuelLeaderBoardsState extends State<DuelLeaderBoards> {
List<Entry> str = [];
GlobalKey<EasyRefreshState> _easyRefreshKey = new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshHeaderState> _connectorHeaderKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>();
GlobalKey<RefreshFooterState> _connectorFooterKey = new GlobalKey<RefreshFooterState>();
Future<LeaderBoards> getLeaderBoards(start) async {
String apiURL = 'https://stats.quake.com/api/v2/Leaderboard?from=$start&board=duel&season=current';
final response = await http.get(apiURL);
if (response.statusCode == 200) {
final responseBody = leaderBoardsFromJson(response.body);
return responseBody;
} else {
throw Exception('Failed to load Data');
}
}
void updateLeaderBoardList(e) async {
setState(() {
str.addAll(e.entries);
});
}
#override
void initState() {
getLeaderBoards(0).then((onValue) => str = onValue.entries );
super.initState();
}
#override
Widget build(BuildContext context) {
Widget header = ClassicsHeader(
key: _headerKey,
refreshText: "pullToRefresh",
refreshReadyText: "releaseToRefresh",
refreshingText: "refreshing...",
refreshedText: "refreshed",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
Widget footer = ClassicsFooter(
key: _footerKey,
loadHeight: 50.0,
loadText: "pushToLoad",
loadReadyText: "releaseToLoad",
loadingText: "loading",
loadedText: "loaded",
noMoreText: "Finished",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
return FutureBuilder(
future: getLeaderBoards(0),
builder:
(BuildContext context, AsyncSnapshot<LeaderBoards> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Builder(builder: (BuildContext context) {
return Center(
child: new EasyRefresh(
key: _easyRefreshKey,
behavior: ScrollOverBehavior(),
refreshHeader: ConnectorHeader(
key: _connectorHeaderKey,
header: header,
),
refreshFooter: ConnectorFooter(
key: _connectorFooterKey,
footer: footer,
),
child: CustomScrollView(
semanticChildCount: str.length,
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(<Widget>[header]),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return new Container(
height: 70.0,
child: Card(
child: new Text(
'${index+1}: ${str[index].userName}',
style: new TextStyle(fontSize: 18.0),
),
));
},
childCount: str.length,
)),
SliverList(
delegate: SliverChildListDelegate(<Widget>[footer]),
)
],
),
onRefresh: () async {
await new Future.delayed(const Duration(seconds: 0), () {
setState(() {});
});
},
loadMore: () async {
getLeaderBoards(str.length).then((onValue) => {
updateLeaderBoardList(onValue)
});
},
// loadMore: () async {
// await new Future.delayed(const Duration(seconds: 0), () {
// getLeaderBoards(str.length).then((onValue) => {
// updateLeaderBoardList(onValue)
// });
// });
// },
)
);
});
}
});
}
}
LeaderBoards leaderBoardsFromJson(String str) {
final jsonData = json.decode(str);
return LeaderBoards.fromJson(jsonData);
}
String leaderBoardsToJson(LeaderBoards data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class LeaderBoards {
String boardType;
List<Entry> entries;
int totalEntries;
LeaderBoards({
this.boardType,
this.entries,
this.totalEntries,
});
factory LeaderBoards.fromJson(Map<String, dynamic> json) => new LeaderBoards(
boardType: json["boardType"] == null ? null : json["boardType"],
entries: json["entries"] == null ? null : new List<Entry>.from(json["entries"].map((x) => Entry.fromJson(x))),
totalEntries: json["totalEntries"] == null ? null : json["totalEntries"],
);
Map<String, dynamic> toJson() => {
"boardType": boardType == null ? null : boardType,
"entries": entries == null ? null : new List<dynamic>.from(entries.map((x) => x.toJson())),
"totalEntries": totalEntries == null ? null : totalEntries,
};
}
class Entry {
String userName;
int eloRating;
String profileIconId;
String namePlateId;
Entry({
this.userName,
this.eloRating,
this.profileIconId,
this.namePlateId,
});
factory Entry.fromJson(Map<String, dynamic> json) => new Entry(
userName: json["userName"] == null ? null : json["userName"],
eloRating: json["eloRating"] == null ? null : json["eloRating"],
profileIconId: json["profileIconId"] == null ? null : json["profileIconId"],
namePlateId: json["namePlateId"] == null ? null : json["namePlateId"],
);
Map<String, dynamic> toJson() => {
"userName": userName == null ? null : userName,
"eloRating": eloRating == null ? null : eloRating,
"profileIconId": profileIconId == null ? null : profileIconId,
"namePlateId": namePlateId == null ? null : namePlateId,
};
}
I looked at the documentation of loadMore. Since it says that the body of the function assigned to loadMore should be async, you do not need to use then:
loadMore: () async {
final result = await getLeaderBoards(str.length);
updateLeaderboardList(result);
},
loadMore: () async {
await getLeaderBoards(str.length).then((onValue) => {
updateLeaderboardList(onValue)
});
},
but putting "await" my loader waits for the function to complete before it finishes the animation.

programmatically change language of flutter i18n apps doesn't work in iOS

I used flutter_i18n plugin (Android Studio) to generate i18n.dart(class S) and S.of(context).locale_msg will return the locale string. The main code is shown below.
Language should be changed programmatically by invoking onLocaleChange(locale) when click the button in HomePage. It works well in Android simulator, but won't change language in iOS simulator. Wonder what's wrong with my code?
class _PaperMoonAppState extends State<PaperMoonApp> {
SpecifiedLocalizationDelegate _localeOverrideDelegate;
void onLocaleChange(Locale locale) {
setState(() {
if (appVars.appConfig.changeLanguage(locale)) {
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(locale);
appVars.saveConfig(); //print save config file...
}
});
}
#override
void initState() {
SpecifiedLocalizationDelegate.onLocaleChange = this.onLocaleChange;
appVars.loadConfig().then((AppConfig _config) {
appVars.appConfig = _config;
setState(() {
_localeOverrideDelegate =
new SpecifiedLocalizationDelegate(appVars.appConfig.getLocale());
});
});
_localeOverrideDelegate =
new SpecifiedLocalizationDelegate(Locale('zh', ''));
super.initState();
}
#override
Widget build(BuildContext context) {
print(_localeOverrideDelegate.overriddenLocale);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Paper Moon",
color: Colors.blueAccent,
localizationsDelegates: [
_localeOverrideDelegate,
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: const <Locale>[
Locale("ja", ""),
Locale("en", ""),
Locale("zh", ""),
],
localeResolutionCallback:
S.delegate.resolution(fallback: _localeOverrideDelegate.overriddenLocale),
home: HomePage(),
// routes: _buildRoutes(),
);
}
}
Custom LocalizationDelegate:
class SpecifiedLocalizationDelegate
extends LocalizationsDelegate<WidgetsLocalizations> {
//class static vars:
//onLocaleChange should be bind to MaterialApp function containing setState().
static LocaleChangeCallback onLocaleChange;
// for instance
final Locale overriddenLocale;
const SpecifiedLocalizationDelegate(this.overriddenLocale);
#override
bool isSupported(Locale locale) => overriddenLocale != null;
#override
Future<WidgetsLocalizations> load(Locale locale) =>
S.delegate.load(overriddenLocale);
#override
bool shouldReload(SpecifiedLocalizationDelegate old) => true;
}
Based on your code, the only thing that seems to be missing is this:
open ios/Runner/Info.plist and add:
<key>CFBundleLocalizations</key>
<array>
<string>ja</string>
<string>en</string>
<string>zh</string>
</array>
As far I as know, by now (march/2019), flutter doesn't yet add automatically the list of supported languages to this file.
I'm using i18n_extensions, but with the same issue...
What worked for me, was use this:
supportedLocales: const <Locale>[
const Locale('en'),
const Locale('pt'),
],
Instead of this:
supportedLocales: const <Locale>[
const Locale('en', 'US'),
const Locale('pt', 'BR'),
],
And then, my i18n.dart. file i've change from this:
extension Localization on String {
static final _t = Translations.from("en_us", {
passwordInput: {
"en_us": "Password",
"pt_br": "Senha",
},
searchingTitle: {
"en_us": "Scanning for devices...",
"pt_br": "Procurando dispositivos...",
},
...
To this:
extension Localization on String {
static final _t = Translations.from("en", {
passwordInput: {
"en": "Password",
"pt": "Senha",
},
searchingTitle: {
"en": "Scanning for devices...",
"pt": "Procurando dispositivos...",
},
It works fine for me.

Flutter: Observable.combineLatest2 is not streaming when page is loaded through navigation

I am creating a flutter app with blocs.
I followed the code available in Flutter login with blocs
It works as expected,
if my app has no routes defined
class App extends StatelessWidget {
Widget build(BuildContext context) {
return Provider(
child: MaterialApp(
title: 'Log Me In!',
home: Scaffold(
body: LoginScreen(),
),
),
);
}
}
but when I change my app to use routes
class App extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Log Me In!',
routes: {
'/':(context) => Provider(
child: Scaffold(
body: LoginScreen(),
),
)
},
);
}
}
bloc code
class Bloc extends Object with Validators {
final _email = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
// retrieve data from stream
Stream<String> get email => _email.stream.transform(validateEmail);
Stream<String> get password => _password.stream.transform(validatePassword);
Stream<bool> get submitValid => Observable.combineLatest2(email, password, (e, p) => true);
// add data to stream
Function(String) get changeEmail => _email.sink.add;
Function(String) get changePassword => _password.sink.add;
submit() {
final validEmail = _email.value;
final validPassword = _password.value;
print('$validEmail and $validPassword');
}
dispose() {
_email.close();
_password.close();
}
}
Observable.combileLatest2 is not streaming the data (but it streams error though).
Using Rxdart version 0.19.0 and
Flutter 1.0.0 • channel beta •https://github.com/flutter/flutter.git
Framework • revision 5391447fae (6 days ago) • 2018-11-29 19:41:26-0800
Engine • revision 7375a0f414Tools • Dart 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)
Am I doing something wrong here?
thanks in advance
After lot of trial, I found that when I use routes for the navigation, flutter will build the page multiple times and thats the expected behavior refer here for detailed answer
So when it builds the page multiple times, it was creating multiple Observables on the bloc as it was creating new instance of Bloc every time it creates the Page route.
So when I modify the code
class App extends StatelessWidget {
final login = Provider(
child: Scaffold(
body: LoginScreen(),
),
);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Log Me In!',
routes: {
'/':(context) => login,
},
);
}
}
it worked perfectly.
The other way is to achieve is to create a stateful widget and do the initialization in the init method.

Resources