I need to call a provider data from a class Which doesn't extend any state classes
class Services {
static listenForMessage(){
var handler = webSocketHandler((webSocket) {
webSocket.stream.listen((message) {
Provider.of<ProviderClass>(context).chatsList.clear();// context unavailable
webSocket.sink.add("echo $message");
});
});
serve(handler, 'localhost', 8080).then((server) {
print('Serving at ws://${server.address.host}:${server.port}');
});
}
}
But there is no context available in this class, how can I call this data without BuildContext
did you try to receive the context in the function arguments and send it where your function is required?
static listenForMessage(context){...}
i have found a link that might help you, not 100% sure
https://maneesha-erandi.medium.com/handling-api-requests-with-flutter-provider-944e3258c5ca
Related
i have just recently stated working with riverpod state mangement in flutter.
i have issue related to comunicate between to state providers.
here is my sample code:
class SomeClass_ONE extends stateNotifer <SomeState> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodOne(SomeState newstatevalue){
state = newstatevalue;
}
}
final someClassOneProvider =
StateNotifierProvider<SomeClass_ONE,SomeState>.
((ref)=>SomeClass_ONE(somevalue: SomeCustomClass()));
now i have another state provider class as below
class SomeClass_Two extends stateNotifer <SomeStateTwo> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodtwo(SomeState newstatevalue){
state = newstatevalue;
}
}
final someClassTwoProvider =
StateNotifierProvider<SomeClass_Two,SomeStateTwo>
((ref)=>someClassTwoProvider(somevalue: SomeCustomClass()));
now what i want to achhive is that on methodOne execution i have to listen that state cahnge and have to trigger methodTow and have to upate secondproviders state as well.
so how can i achive this without using Ref in class cunstroctors?
i have tried with ref.listner to trigger and have passed Ref in both class constructors. but as per some condition i can't use Ref directly in constructors as per some guideline followed by seniors.
You can pass a Ref ref object to the methodtwo method and then call the necessary methods from other StateNotifierProvider. In any case, to refer to other methods of other classes, you need to have a Ref object.
Try to use watch provided by StateNotifierProvider
Try this code:
class SomeClass_ONE extends stateNotifer <SomeState> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodOne(SomeState newstatevalue){
state = newstatevalue;
// Listen to the changes in the state of the first provider and call the methodtwo of the second provider
someClassTwoProvider.watch((_) => _.methodtwo(newstatevalue));
}
}
Consider the web controller that implements some API by wrapping downstream service that requires token to be called. The token has the expiration, so I'm after some kind of time-driven scope that re-acquires the token and re-creates client in case the token is expired:
MyController: Controller
{
IServiceAPI _downstreamServcie;
MyController (IServiceAPI downstreamService)
{
}
}
....
builder.Register(c => {
Token token = generateToken() ..
return new ServiceAPIClient(token) ;
})
.As<IServiceAPI>()
I don't want to register MyController with per-request-scope because of performance issues.
Having spring background, such kind of captive dependency is resolved in spring by injecting singleton dynamic proxy that forwards the call to the right scoped-object (request/session/custom).
What would be the right way to implement the same with Autofac?
Thanks
[UPDATE]
Digging into Autofac documentation, I've found IResolveMiddleware interface that can be used to dynamically create/change scope :
class TokenScopeResolverMiddleware : IResolveMiddleware {
private ISharingLifetimeScope _currentTokenScope;
private ISharingLifetimeScope _prevTokenScope;
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next) {
if (null == _currentTokenScope) {
lock (this) {
if (null == _currentTokenScope) {
RolloverScope(context);
}
}
}
if (!CanUseCurrentToken()) {
lock (this) {
if (!CanUseCurrentToken()) {
RolloverScope(context);
}
}
}
context.ChangeScope(_currentTokenScope);
next(context);
}
private bool CanUseCurrentToken() {
AuthenticationResult authResult = _currentTokenScope.Resolve<AuthenticationResult>();
TimeSpan expiresIn = authResult.ExpiresOn - DateTime.Now;
return expiresIn > TimeSpan.FromSeconds(20);
}
private void RolloverScope(ResolveRequestContext context) {
if (null != _prevTokenScope) {
_prevTokenScope.Dispose();
}
_prevTokenScope = _currentTokenScope; // give another `expiration time` grace period before disposing token scope
_currentTokenScope =
context.ActivationScope.RootLifetimeScope.BeginLifetimeScope("token") as ISharingLifetimeScope;
}
public PipelinePhase Phase { get; } = PipelinePhase.ScopeSelection;
}
Usage :
builder.Register(c => {
AuthenticationResult result = // acquire token
return result;
})
.InstancePerMatchingLifetimeScope("token");
builder.Register(c => {
return new Client(c.Resolve<AuthenticationResult>().Token)
})
.InstancePerMatchingLifetimeScope("token");
builder.RegisterServiceMiddleware<Client>(new TokenScopeResolverMiddleware());
Any better suggestions ?
I think you're likely looking for the Func<T> relationship, or something like it, where you inject a factory that dynamically resolves the client as you need it.
public class MyController
{
private readonly Func<IClient> _clientFactory;
public MyController(Func<IClient> clientFactory)
{
this._clientFactory = clientFactory;
}
public void DoWork()
{
var client = this._clientFactory();
client.CallApi();
}
}
Your lambda could be just about anything as long as it runs synchronously. Don't forget DI is more about injecting dependencies (object construction) than it is about managing your application's state, orchestrating logic, or executing factories on your behalf, though admittedly it's pretty convenient to try to multipurpose it in those ways.
var builder = new ContainerBuilder();
builder.Register(ctx =>
{
var token = GetOrRefreshToken();
return new Client(token);
}).As<IClient>();
A word of warning - you may run into memory leak trouble.
If the IClient implementation is also IDisposable, Autofac is going to hold onto every IClient created until the lifetime scope is disposed because the container is responsible for creating objects... and disposing them. If your controller is a singleton, that means the Func<IClient> will be resolving from the root lifetime scope (the container itself), which further means you can't dispose the captured IClient instances without disposing the whole application container.
You can disable that with ExternallyOwned but then you also will have to dispose things yourself.
It may be better to unwind things just a little and try to do less in DI, more with your own code. For example, actually create your own client factory that knows when to refresh the token, how to construct and dispose of clients, etc. You may even want to look at stuff like IHttpClientFactory which is specifically meant for stuff like this. Then instead of injecting the client, inject the factory and use the factory to get a client instance as you need it. That is, instead of injecting Func<IClient>, inject IHttpClientFactory or something similar, thus reducing the need to try to force the captive dependency to behave and instead addressing the challenge with a solution possibly more appropriate.
I wrote an API class that, obviously, encapsulates a couple http requests. However, this API class also stores a little bit of state: Namely, it saves an authentication token that will be used in all subsequent requests after the first.
In my flutter function, I then first wait for that token to be set before doing anything else:
Future<void> main() async {
var api = MyAPI();
await api.auth.refreshTokens(); // Sets api.auth.token
runApp(MaterialApp(etc));
What is important here is that in etc I am also setting Providers, one of which being that very API:
providers: [Provider(create: (context) => api), ...]
which I then want to use all throughout my app. However, is that really the best way to go about it? It seems like a really really cumbersome and messy approach to simply be able to use this more or less global API object.
How is it usually done?
You can write that class as a singleton, this way you can call the constructor and obtaining each time the same instance without creating a new one:
void main() {
Api().call();
Api().call();
}
class Api {
static Api? _instance;
Api._();
factory Api() {
_instance ??= Api._();
return _instance!;
}
String token = '';
void call() {
token += '*';
print('new token $token');
}
}
I tried to mock database to test my local api, i search in official document finding mockito which can work with remote api fine, but also can not work with local database out of box, is there any way to work around of it?
In these cases, you have two options (among many others). Even if my examples assume you're making HTTP calls, it doesn't matter. You can use these strategies regardless the specific use case I'm exposing!
The first one is using the "Strategy pattern" to create an interface for the API and then switch between a test and a production API. Here's a simple example:
abstract class HttpRepository {
const HttpRepository();
Future<Something> sendRequest();
}
You can now create 2 concrete classes: one is for the actual API call and the other is just a mock for tests.
/// Use this in your tests
class MockHttpRepository extends HttpRepository {
const MockHttpRepository();
#override
Future<Something> sendRequest() async {
// Simulating the HTTP call
await Future.delayed(const Duration(seconds: 2));
return Something();
}
}
/// Use this in your Flutter code to make the actual HTTP call or whatever else
class ApiHttpRepository extends HttpRepository {
const ApiHttpRepository();
#override
Future<Something> sendRequest() async {
// Doing a real HTTP call
final response = await makeGetOrPost();
return Something.withData(response);
}
}
In this way, you'll use ApiHttpRepository in your Flutter app and MockHttpRepository in tests. Use const constructors whenever possible.
The other way is using mocks to simulate fake HTTP calls or anything else. Basically, you're using when to "trap" a method call and return a fake response you can control.
// 1. "Enable" mocking on your type
class MockRepo extends Mock implements ApiHttpRepository {}
// 2. Mock methods
const ApiHttpRepository repo = MockRepo();
when(repo.sendRequest()).thenAnswer((_) async => Something());
In this case, we're using thenAnswer because the return type of sendRequest() is of type Future<T>. In your case, if you are reading data from a database you just need to:
Make your class "mockable" using extends Mock implements YourClass
Use when on the mockable instance and control the output
Make sure to use thenAnswer if the method returns a Future<T> and thenReturn in all the other cases.
I have an Api class that accepts an (optional) authentication token that it uses for making authenticated requests e.g. Api(token: 'a9sa2ksas12').getUserDetails().
If the token is not passed, it has to perform the relatively expensive operation of reading it from sharedPreferences.
class Api {
static const BASEURL = "https://api.google.com/";
final String token;
Api({ this.token });
Future<http.response> getUserDetails() async {
return http.get('$BASEURL/user/', headers: { 'Authorization': token });
}
}
How can I setup my app so that the token is read only once from sharedPreferences and used throughout the app for all future Api() requests?
Some ideas I've considered and think that may work:
Have token be a global variable
Make the API class a singleton, and pass it around between "screens"
Well in general there's nothing bad in making you Repositories a singletons. But on the other hand, I don't like the concept of passing the API classs between the screens.
When widgets use your data source directly without any middleman, like Bloc or Provider, they tend to be polluted with a lot of presentation logic. I personally prefer to separate those concerns and let widgets do what they are made for: rendering the UI. This makes them smaller and easier to test.
What's more the API class should be responsible only for the network calls. It shouldn't be responsible for managing the token. I'd inject to the API class something like:
class TokenProvider
{
Future<String> getToken();
}
The responsibility of this class would be, you guessed it, to provide a token. It can return cached value or get it from the SharedPreferences. Thanks to this the API doesn't have to care where the token comes and how to handle it. It will do just one thing: api calls.
I ended up using the "Locator" pattern, via get_it.
The code was pretty simple.
Step 1: Setup a top-level locator.dart in lib/
// ./lib/locator.dart
import 'package:my_app/services/api.dart';
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(() => Api());
}
Step 2: Use api anywhere in the app by just importing the locator:
// ./lib/widgets/some_screen.dart
class _SomeScreenState extends State<SomeScreen> {
Api api = locator<Api>();
#override
void initState() {
api.getUserDetails().then((response) => {
// do anything you like with the response
});
super.initState();
}
The beauty of this approach is that Api is only ever initialized ONCE in the lifetime of the app, so I simply assign a token to it on initState without worrying about the widget getting disposed/rebuilt and repeated fetches to SharedPreferences.