How to use shelf_router with shelf_static in Dart/Flutter? - flutter

I am trying to create a web server using Shelf in Dart. I have successfully built any api. My router.dart looks like this:
import 'package:dart_shelf_server_sample/files/controller.dart';
import 'package:dart_shelf_server_sample/helpers/helpers.dart';
import 'package:dart_shelf_server_sample/posts/controller.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
Router routes() {
var app = Router();
app.get('/api', (Request request) {
var response = {
'message': 'Dart API is alive',
'api_routes': ['/posts', '/posts/{id}']
};
return Response.ok(toJson(response));
});
app.get('/posts', (Request request) {
return PostController().find();
});
app.get('/posts/<id>', (Request request, String id) {
return PostController().findOne(id);
});
app.get('/files/<name>', (Request request, String name) {
return FileController().findOne(name);
});
return app;
}
Here is my server.dart:
import 'package:dart_shelf_server_sample/config/constants.dart';
import 'package:dart_shelf_server_sample/config/routes.dart';
import 'package:shelf/shelf_io.dart' as io;
void main(List<String> args) async {
var app = routes();
var server = await io.serve(app, hostname, port);
print('Serving at http://${server.address.host}:${server.port}');
}
Now, what I am trying to achieve is to serve files from the webserver based on the requested path through a custom FileController class. How do I use shelf_static to implement this feature?
Thanks.

Try
app.mount('/files/', createStaticHandler('path/to/static/files'));
If you have a file path/to/static/files/index.html it will be served for the request /files/index.html.

Related

Is there currently a way to compute a dart function with webworkers?

I am currently trying to run a A* seek computation on flutter web
class AStarSeeker extends Seeker {
#override
Future<Path> Function() seek(SeekComputationInput input) {
return () => compute(doComputation, input);
}
}
Future<Path> doComputation(SeekComputationInput input) async
{
return Path.empty;
}
But I read that compute() does not currently work on flutter web. Is there a way that I can run my dart function with a webworker? I read that you can run js functions but couldn't find anything about dart functions.
Sounds like you can use raw Isolates in Dart. Read the official doc about isolates, which are just like threads in other languages.
By the way this link may be helpful: Dart: in browser webworker written in dart and used in dart2js and https://api.dart.dev/stable/2.16.0/dart-html/Worker-class.html
UPDATE
Sounds like isolate does not work in web. Then what about the "Worker" approach I mentioned above. Notice that you can always compile your dart code to a js file, so even if Worker only supports js you can still execute dart there.
This https://dev.to/kyorohiro/isolate-at-flutter-for-web-28lg sounds like a good tutorial.
The core part:
parent.dart
import 'dart:html' as html;
main() async {
if(html.Worker.supported) {
var myWorker = new html.Worker("ww.dart.js");
myWorker.onMessage.listen((event) {
print("main:receive: ${event.data}");
});
myWorker.postMessage("Hello!!");
} else {
print('Your browser doesn\'t support web workers.');
}
}
child.dart
import 'dart:async';
import 'dart:html' as html;
import 'dart:js' as js;
import 'package:js/js.dart' as pjs;
import 'package:js/js_util.dart' as js_util;
#pjs.JS('self')
external dynamic get globalScopeSelf;
Stream<T> callbackToStream<J, T>(String name, T Function(J jsValue) unwrapValue) {
var controller = StreamController<T>.broadcast(sync: true);
js_util.setProperty(js.context['self'], name, js.allowInterop((J event) {
controller.add(unwrapValue(event));
}));
return controller.stream;
}
void jsSendMessage( dynamic object, dynamic m) {
js.context.callMethod('postMessage',[m]);
}
main() {
callbackToStream('onmessage', (html.MessageEvent e) {
return js_util.getProperty(e, 'data');
}).listen((message) {
print('>>> ${message}');
jsSendMessage(js.context, 'callback: ${message}');
});
}

How to connect a Phantom wallet to my Flutter web app?

I've been trying unsuccesfully to connect a Flutter web app to a Phantom wallet. No pub.dev packages have been released in order to accomplish this and can't figure out how to do it with dart-js interop.
Wondering if someone already figured it out?
I have a (crude) working piece of code that could be useful for somebody trying to accomplish the same:
// web/index.html
<script src="../lib/wallet.js" />
// wallet.js
class ClientWallet {
constructor() {
this.pubKey = '';
}
async connect() {
const resp = await window.solana.connect();
this.pubKey = resp.publicKey.toString();
}
address() {
return this.pubKey;
}
disconnect() {
window.solana.disconnect();
}
}
var walletModule = { ClientWallet: ClientWallet };
// main.dart
import 'package:js/js.dart';
import 'package:js/js_util.dart';
#JS('walletModule.ClientWallet')
class ClientWallet {
external Future<void> connect();
external void disconnect();
external String get pubKey;
}
Future<void> connectWallet() async {
ClientWallet wallet = ClientWallet();
await promiseToFuture(wallet.connect());
}
And then for connecting simply call connectWallet(). This works for me for the Phantom wallet, now I'm trying to integrate the Solana Dart package for signing a transaction.

How to Access Provider with out Context in Flutter

I have a problem with the Flutter Provider pattern, I need to access Provides from Class where I don't have Context.
Providers :
import 'package:flutter/foundation.dart';
class TokenProvider with ChangeNotifier {
TokenService tokenService = TokenService();
String _accessToken = '';
String get accessToken {
return _accessToken;
}
dynamic setAccessToken(data) async {
_accessToken = data;
}
}
Class :
import '../constants/constants.dart';
import '../models/models.dart';
import './network-call/base-service.dart';
class TokenService extends BaseService {
Future<String> getToken() async {
final dynamic response = await serviceCall(
url: ApiName().apiName(api: ServiceName.TOKEN),
method: ApiMethod.POST,
queryParameters: {
'id': Preferences().env['id'],
'secret': Preferences().env['secret'],
'type': 'rrrr'
});
Need to set this responce Data in Providers
}
}
Thank you.
try to add "notifyListeners();"
dynamic setAccessToken(data) async {
_accessToken = data;
notifyListeners();
}
I use to pass the context when needed, as dlohani suggests in the comment at the question, but I found myself in the same situation and applied a solution inspired by the communication pattern used between isolates: messages exchange.
The Provider class need to have a ReceiverPort field which listens to request messages. Ones the message reaches this listener you're inside the Provider, so you can retrieve data and send them back, again in the ReceiverPort's fashion, that is using the sendPort of the ReceiverPort registered in the requesting class.
In code below I suppose messages are Maps to clarify the type of data exchanged:
class SomeProvider with ChangeNotifier {
var _innerData = yourData;
var _providerReceiverPort = ReceiverPort();
SomeProvider() {
// The registration is necessary to "publish" the receiver port
IsolateNameServer.registerPortWithName(
_providerReceiverPort, "SomeProviderPort");
_providerReceiverPort.listen(
(message) {
// I retrieve the port to send the response to
var port = message["sendPort"];
// The answer follows the rules of messaging: maps and lists are ok
port.send({"data": _innerData.getSomething()});
}
);
}
}
class SomeClient {
var _clientReceiverPort = ReceiverPort();
someFunction(){
// First step: prepare the receiver to obtain data
_clientReceiverPort.listen(
(message) {
// Data are stored in the map containing a "data" key
var response = message["data"];
...
}
);
// Now I can retrieve the provider port by using the same name it uses to publish it
var providerPort = IsolateNameServer.lookupPortByName("SomeProviderPort");
// The message must include the sendPort to permit the provider to address the response
providerPort.send({"sendPort": _clientReceiverPort.sendPort});
}
}
The drawback of this solution is that the Provider doesn't work as a provider for the SomeClient class. You can obviously notify if any change in the listener is important for the subscribers: for example, I use this pattern to update data in the provider from a background isolate.
As I said, this is a workaround, any suggestion to improve is welcome.

How to handle two calls and the loading controller in Ionic 4

I have a requirement where I have 2 API calls, and I want the first two calls to be there for the first request. And 2nd API call to be there when navigated back.
I am calling 1st API in ngOnInit webhook and 2nd API on ionViewWillEnter webhook.
The issue which I am facing is sometimes my loader doesn’t get dismissed when both of the request complete at the same time.
So the possible solution which I am thinking is that if I could call both APIs on the first load synchronously and thereafter call another API every time the back button is clicked.
NOTE: I am using loaders in my interceptor.
CODE: For interceptor
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Clone the request to add the new header.
const authReq = req.clone();
this.showLoading();
// send the newly created request
return next.handle(authReq).pipe(catchError((error) => {
if (error.status === 401) {
this._navCtrl.navigateForward('/login');
}
return throwError(error);
}), finalize( () => {
console.log('hi');
this.dismissLoading();
})
);
}
EDIT:
Code to show loader and hide loader:
async showLoading() {
return await this._loadingCtrl.create().then(a => {
a.present();
});
}
async dismissLoading() {
return await this._loadingCtrl.dismiss();
}
In my case, I will create a LoaderService to handle the Loading by myself. The special thing is I will create a flag called isShowing, so if the loading is already showing, we just need to update the loading message by calling presentLoader function again. There will be only one Loading popup show up on your screen.
In your case, I would not recommend to display the Loader in HTTP Interceptor because we cannot handle the HTTP call stack there. Just create a new function that combines all necessary API calls and show/dismiss popup when you start/finish processing the data.
import { LoadingController } from '#ionic/angular';
import { Injectable } from '#angular/core';
#Injectable()
export class LoaderService {
private loading: HTMLIonLoadingElement;
private isShowing = false;
constructor(private loadingController: LoadingController) {}
public async presentLoader(message: string): Promise<void> {
if (!this.isShowing) {
this.loading = await this.loadingController.create({
message: message
});
this.isShowing = true;
return await this.loading.present();
} else {
// If loader is showing, only change text, won't create a new loader.
this.isShowing = true;
this.loading.message = message;
}
}
public async dismissLoader(): Promise<void> {
if (this.loading && this.isShowing) {
this.isShowing = false;
await this.loading.dismiss();
}
}
}
The simple solution would be to make a function call whenever you click the bak button and inside the function you can make a API call.
Instead of linking to the back button you can use ionViewWillEnter, which is called whenever you are about to leave a page but the downside would be it is called every time view is changed regardless of the fact that only when back button is clicked.
Also you should check, is your service singleton and it creates a single instance of ionic-loader. I think in your case more than one instance of loader is being created.
Also instead of calling the loader in interceptor, you can call showLoading() in ionViewWillEnter and hideLoading() in ionViewDidEnter() of your page.
You can create a Singleton Loader Service as shown below.
This service will take care of creating only a single instance of ionic loader.
import { Injectable } from '#angular/core';
import { LoadingController } from '#ionic/angular';
#Injectable({
providedIn: 'root'
})
export class LoaderService {
private loader: HTMLIonLoadingElement;
constructor(private loadingController: LoadingController) {}
async showLoader() {
if (!this.loader) {
this.loader = await this.loadingController.create({ message: 'Loading' });
}
await this.loader.present();
}
async hideLoader() {
if (this.loader) {
await this.loader.dismiss();
this.loader = null;
}
}
}
private loading: HTMLIonLoadingElement;
constructor(public loadingController: LoadingController) { }
public async show(): Promise<void> {
return await this.loadingController.create({
message: 'Please wait...',
spinner: 'crescent'
}).then(a => {
a.present().then(() => {
console.log('presented');
});
});
}
return await this.loadingController.dismiss().then(() =>
console.log('dismissed'));
}`enter code here`

How to get JSON data via http get request

I am new to Vaadin.
As in topic I would like to make http get reaquest in order to retieve some JSON data.
How could I do this ?
I have been trying to make this by com.google.gwt.http.client.RequestBuilder, but I have obtained
java.lang.UnsatisfiedLinkError: com.google.gwt.xhr.client.XMLHttpRequest.create().
I think the error is associated to GWT client - side nature.
So how could I make http get request in Vaadin 7 server - side ?
Here is my code:
package com.example.soaclient;
import javax.servlet.annotation.WebServlet;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
#SuppressWarnings("serial")
#Theme("soaclient")
public class SoaclientUI extends UI {
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = SoaclientUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
Button button = new Button("Click Me");
button.addClickListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
layout.addComponent(new Label("Thank you for clicking"));
String url = "some URL goes here";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// Couldn't connect to server (could be timeout, SOP violation, etc.)
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
// Process the response in response.getText()
} else {
// Handle the error. Can get the status text from response.getStatusText()
}
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
}
});
layout.addComponent(button);
}
}
With Vaadin you should not use anything from com.google.gwt.http.client package. That is only for client side development, e.g. when you make components that need client side counterparts.
Instead of GWT classes you should just stick to generic JDK libraries. E.g. you could simply use java.net.URL.openStream(). But if you are consuming some REST services, you could refer to my recent JAX-RS 2.0 Client article.