How to prevent dropping event when it is a broadcast stream? - stream

I want to prevent flutter from dropping broadcast stream event when no widget is listening. Because I want to listen to this in other page routes.
I have bloc object like below.
class UserBloc extends Object {
final _user = StreamController<UserModel>.broadcast();
final _uid = StreamController<String>.broadcast();
Stream<UserModel> get user => _user.stream;
Stream<String> get uid => _uid.stream;
Function(UserModel) get setUser => _user.sink.add;
Function(String) get setUID => _uid.sink.add;
I am listening to those streams in my sub page route
StreamBuilder(
stream: userBloc.user,
builder: (context, snapshot) {
print(snapshot.connectionState);
print(snapshot.data);
return Text('hi');
},
),
It works without using broadcast stream. However, no luck when I changed to broadcast.
The problem is broadcast streams drop events when no one is listening but I want to start listening when my other MaterialPage pops up.
Using broadcast stream, data is always null in stream when I started listening.
How can I achieve this?

Replace the StreamController by BehaviorSubject from RxDart package.
A special StreamController that captures the latest item that has been
added to the controller, and emits that as the first item to any new
listener.

Related

How to close receivePort to get .toList() method working?

I am trying to get answer from Isolate as list. I wrote next code. The problem That it's not working. It's simply wait.
main() async
{
ReceivePort receivePort = ReceivePort();
Isolate.spawn(echo, receivePort.sendPort);
var d = await receivePort.toList();
receivePort.close();
print(d);
}
echo(SendPort sendPort) async
{
ReceivePort receivePort = ReceivePort();
sendPort.send("message");
}
The receivePort is a stream. When you call toList on the stream, it waits for the stream to complete. That never happens, so the toList call stalls forever.
If you know that the other end of the communication only sends one message, then you can instead do var d = await receivePort.first;. This only waits for the first message.
In general, when doing isolate communication, the sending isolate should send a special message when they are done, because the receiving isolate has no other way to know that. That is: You want a communication protocol, so the receiver can know whether there are going to be more messages. Maybe each message is an object which contains an isLast boolean, or the last message is null, or you wrap all messages in subclasses of a Message class that defines your protocol. What to do depends on the actual use-case.
The only rule is: You have to say when you are done.
You should use .listen() instead of await for, if you want to see output of the list before is stream closed. listen will register the handler and execution keeps continue.
instead of
var d = await receivePort.toList();
receivePort.close();
print(d);
try
receivePort.listen((data) => {print(data)});
You can find differences with listen and await here

Login page that retrieves a token

I have an app that requires users to log in. I have the website for the login page and everything.
So I've been using the "flutter_webview_plugin", "openid_client" "flutter_appauth" packages. At the moment I have a webview that opens the login page. So the home page is just a button that routes to a class called "LoginScreen()", which then shows the web page. That's what you see in the code. I am fairly new to flutter and programming as a whole, so please explain as if I have no idea what I'm doing.
Another note is that I am unsure where to apply the logic for the authentication and token retrieval. i.e. in the home page or login page where the webview is
import 'dart:async';
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:flutter/services.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final flutterWebviewPlugin = new FlutterWebviewPlugin();
StreamSubscription _onDestroy;
StreamSubscription<String> _onUrlChanged;
StreamSubscription<WebViewStateChanged> _onStateChanged;
String token;
#override
void dispose() {
// Every listener should be canceled, the same should be done with this stream.
_onDestroy.cancel();
_onUrlChanged.cancel();
_onStateChanged.cancel();
flutterWebviewPlugin.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
//flutterWebviewPlugin.close();
// Add a listener to on destroy WebView, so you can make came actions.
_onDestroy = flutterWebviewPlugin.onDestroy.listen((_) {
print("destroy");
});
_onStateChanged =
flutterWebviewPlugin.onStateChanged.listen((WebViewStateChanged state) {
print("onStateChanged: ${state.type} ${state.url}");
});
// Add a listener to on url changed
_onUrlChanged = flutterWebviewPlugin.onUrlChanged.listen((String url) {
if (mounted) {
setState(() {
print("URL changed: $url");
//flutterWebviewPlugin.close();
});
}
});
}
#override
Widget build(BuildContext context) {
String url = "http://login/page";
return WebviewScaffold(
appBar: AppBar(
title: Text("Login"),
),
url: url,
withZoom: false,
);
}
}
The webview works, but I have no idea how implement the logic
You're going about this the complete wrong way. Flutter is meant to provide an easier to build native experience. You're using a more advance technology than native programming and making it depend on a lesser technology which is wrapping webviews in a native app. I would suggest the following. If you can implement them.
Expose your API for your login publicly so that you can access it from the mobile app directly.
Build your own login screen on Flutter and don't wrap a website using Flutter.
Once your API is exposed, return your token as a normal json object and use it in the application as you wish.
If you can't change the api and really want to wrap your application (which I don't recommend) then don't use Flutter. Flutter's webview is very limited (It's still in developers preview as it depends on adding Native views into the Element tree, which is an upcoming feature). Use a technology that's catered towards wrapping websites and that provides built in support. If you're new to programming this will be very confusing so use something that's catered towards what you want to do.
I'd recommend looking at the following technologies:
Cordova (which is going out of fashion Very quick)
Ionic More popular and is kind of still growing amongst the native hybrids even though it's completely web based.
I know it's not the answer you were probably looking for but based on your question you're going to have a much harder time with solving this problem. Please try and follow the first points I laid out, your development experience will be much better and your solution will benefit as well in terms of simplicity.

Flutter/dart- download data from mysql server database

To preface this i am new to all of this and don't need step by step information, just seeing what is possible.
I've been toying around with connecting my flutter/dart app to a mysql backend
Im connecting to it using sqljocky and was wondering if there is anyway users can download data from it for offline use. I read about NSUserDefaults (for ios) and Shared_Preferences (for android) for storage of persistent and simple data on the app and wanted to know if this is the correct route to continue on.
I think this answer is required here:
Few days ago I had to dealt with a existing MySQL database and there was no way to do it with Dart except over network requests(there is a sqljocky package which is Dart 2 incompatible). Read best options here.
Here Im going to show an example for a simple fetch data using Flutter http request with simple PHP & MySQL.
Note: This is may not 100% secure.
If you are doing in local server, you need to have server(ex: apache) running and MySQL(you can use XAMPP to get both).
Step 1: create a database.
Step 2: create a folder in htdocs(in XAMP). Next, inside that file create file called conn.php. Then, add below snippet and change
with your db credentials to connect to database.
<?php
$connection = new mysqli("localhost", "username", "password", "db_name");
if (!$connection) {
echo "connection failed!";
exit();
}
?>
Note: above snippet is a simple example and you can customize as you want.
Step 3: Create php file called fetch_data.php and copy paste below code snippet:
<?php
include("conn.php");
$queryResult = $connection->
query("SELECT * FROM your_table");//change your_table with your database table that you want to fetch values
$result = array();
while ($fetchdata=$queryResult->fetch_assoc()) {
$result[] = $fetchdata;
}
echo json_encode($result);
?>
Above code snippet will fetch the data from db table and display it after json_encode.
Step 4: Open up your Flutter project or create one. Below is a code snippet that requesting for fetched data and display them using a ListView.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Login",
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List data = [];
#override
void initState() {
fetchData();
super.initState();
}
void fetchData() async {
final response = await http.get('http://10.0.2.2/fluttertest/fetch_data.php');
if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(data[index]['title']),
)),
);
}
}
There is an important thing, you should keep in mind: 10.0.2.2 or your machine IP address(type ipconfig in cmd to get your IP address) have to use because emulator itself localhost(127.0.0.1).
Note: I didn't use any model or advanced things to show you bare minimum example. If you want a great CRUD example, check my github repo and you can also do some pull requests because it's can extended.
If you want to insert or update data, you can use http.post() and in php use INSERT, UPDATE sql statements(It's better using Prepared Statement to prevent sql injections).
Also you can read my article. There I have explained everything using a CRUD application.
For Android we can use SQLite and for Flutter, we have equivalent SQFlite that support both IOS and Android.
And there are other best solutions like Firebase database, Firestore and you can also create API and access through HTTP. Also you can store data in a JSON file.
For you to continue on these may help:
Using mysql in flutter.
Flutter connecting to a database in server.

stomp disconnects its processing twice in channel interceptor and SimpleBrokerMessageHandler

I have modified to implement channel interceptor in spring-websocket-portfolio sample application (https://github.com/rstoyanchev/spring-websocket-portfolio). whenever the client disconnects, channel interceptor is processed twice. I have similar implementation in my production application. As it is being invoked twice so it has unwanted result for the 2nd invocation. I had put work around for the time being. But wondering why my channel interceptor is invoked twice? Any help would be highly appreciated.
modified items: WebSocketConfig.java:
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(channelInterceptor());
}
#Bean
public ChannelInterceptor channelInterceptor() {
return new ChannelInterceptor();
}
ChannelInterceptor :
package org.springframework.samples.portfolio.config;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
public class ChannelInterceptor extends ChannelInterceptorAdapter {
#Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
System.out.println(sha.getCommand() + " " + sha);
switch (sha.getCommand()) {
case CONNECT: {
System.out.println("connected:"+sha.getSessionId());
break;
}
case DISCONNECT: {
System.out.println("disconnected:"+sha.getSessionId());
break;
}
default:
System.out.println("default:"+sha.getCommand());
break;
}
}
}
logs:
**disconnected**:9k1hvln6
**disconnected**:9k1hvln6
Disconnect events may happen more than once for the same session, your interceptor should be idempotent and ignore duplicate events.
You may also consider using application events (SessionConnectEvent, SessionDisconnectEvent...) instead of a channel interceptor. Here's an example of an idempotent event listener: https://github.com/salmar/spring-websocket-chat/blob/master/src/main/java/com/sergialmar/wschat/event/PresenceEventListener.java
Generally a DISCONNECT frame comes the client side, is processed in the StompSubProtocolHandler, and is then propagated to the broker. However, a connection can also be closed or lost without a DISCONNECT frame. Regardless of how a connection is closed, the StompSubProtocolMessageHandler generates a DISCONNECT frame. So there is some redundancy on the server side to ensure the broker is aware the client connection is gone.
As Sergi mentioned you can either subscribe to listen for SessionDisconnectEvent (of which there should be only one) and other AbstractSubProtocol events or ensure your code is idempotent.

How to build a mobile app from scratch with Flutter and maybe Rails?

I'm a Rails web back & front dev, and I want to build a Mobile app. A simple app, with products, users, geolocalisation and maybe payment (with a third-part like Stripe).
I think Flutter framework is a good choice, looks verry simple.
But I don't know how a thing of the Dart language (or mobile native dev), and don't know where to start.
I thought of a system like :
Rails back-end for products and users
Flutter framework app for actions and geolocalisation
API between them to receive and send data
Do you have some advices for me ? Where to start, and alternatives ?
Many thanks !
Your question should should definitely not be so broad since there's no right answer to this but I'll try. I learned flutter in a a few days using the documentation only.
I learned the setup, installation and wrote my first app following this which is in the docs.
I learned architecture through looking at this website and just reading more about the particular architecture that's being implemented.
To get better at the layouts itself, which is super easy and nice to deal with, I had this old design challenge of mine on instagram and I implemented one UI every day for a few days using flutter. After about a week I could build any layout I wanted too.
I've settled on using scoped model as described here and redux in larger apps. It's pretty awesome.
This is all I used, then weekly I would watch the widget of the week on google's developer youtube page and that's it. Flutter's medium community is very active and should be a good source of info, but I almost never read blogs on there unless I need to learn something new.
It's pretty simple to connect a Rails API to a Flutter UI, there are a couple of considerations around state management but Google recommend using the BLoC pattern and it works really well with Rails.
Now I'm not going to go into the details around implementing the BLoC pattern but I will leave some links at the bottom around this topic.
First step is to check the flutter docs, they have some decent Cookbooks and I will adapt one below using Rails generators. https://flutter.dev/docs/cookbook/networking/fetch-data
Create a Post resource:
rails g resource post title:string body:string
Run rails db:migrate.
Create a post using a rails console
In your flutter app (main.dart) I'm assuming it's a brand new app here:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Post> fetchPost() async {
final response =
await http.get('http://10.0.2.2:3000/posts/1');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int id;
final String title;
final String body;
Post({this.id, this.title, this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
void main() => runApp(MyApp(post: fetchPost()));
class MyApp extends StatelessWidget {
final Future<Post> post;
MyApp({Key key, this.post}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
start a rails server running on port 3000
start the flutter app
That's about it really. Rails and Flutter are absolutely wonderful together and with Flutter Web coming out... at some point... I'm really excited.
Some other resources:
Flutter and Rails CLI (Still very much a WIP but getting there) https://rubygems.org/gems/frap
State management:
https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/
https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1
A bunch of examples
https://github.com/flutter/samples/blob/master/INDEX.md

Resources