Dynamic tab view with multiple API calls using Flutter - flutter

I want to create a Flutter tab view, tab count and content must be based on the multiple API calls.
There is 3 section in each tab and each section get data from a separate API.
What is the best method to create a solution? Bloc is the best way to manage the state.
I tried to implement a CustomTabView and set tab data but I'm facing various issues and wired tab combinations. Please help me to solve this problem.
Thank you

This is a general question and doesn't have debug details, so the answer can seem a bit dumb.
Anyway,
The best case is to have a separate block for each section.
But you can also have all requests in one block. It is not recommended at all, but I will explain the method.
For call Multi api in one bloC:
when you use the state manager bloC , you have a method named mapEventToState that calls a second method of Stream type according to your request.
It is in this method that you return the State. In order to call several APIs, you need to define a dynamic variable here for each one. Then connect to each API here and fill these variables with the corresponding response.
To return the result, it is better if all variables containing the response containing statusCode were successful, then the state of this bloC should be succeeded.
...
#override
Stream<MyBlocState> mapEventToState(MyBlocEvent event) async* {
if (event is CallTheAPIsEvent) {
yield ApisHaveNeedCalles();
}
// second method of Stream
Stream<MyBlocState> ApisHaveNeedCalles() {
var resultApi1;
var resultApi2;
var resultApi3;
try {
resultApi1 = _repository.getAPI1();
resultApi2 = _repository.getAPI2();
resultApi3 = _repository.getAPI3();
} catch (e) {
yield ErrorState();
}
if (resultApi1 != null && resultApi2 != null && resultApi13 != null) {
yield SuccessState(resultList: [resultApi1, resultApi2, resultApi3 ]);
}
// more event handling
}
...

Related

Bound variable in ViewModel is not updating the displayed value

I was trying to create an countdown timer in ViewModel but i didnt found any method to do that so i ve tried to do this with task delay and while loop but it ends after first task delay. Do u know any other way how to do that or how to repair that one.
public PageViewModel()
{
MethodName();
}
public async void MethodName()
{
CountSeconds = 10;
while (CountSeconds > 0)
{
await Task.Delay(1000);
CountSeconds--;
}
}
The reason why you can`t see others is related to context. You trying to run async code in non-async context.
To solve this problem you can do several ways, which way to choose is your choice and depends on your needs:
await MethodName();
async Task MethodName()
{
CountSeconds = 10;
while (CountSeconds > 0)
{
await Task.Delay(1000);
CountSeconds--;
}
}
Another way is to create various of tasks and execute them, here you can see methods, which can help you.
And as Rand Random metioned it's not about MAUI, it`s about understanding of async programming itself, so for you will be usefull read more about it.
You can use Dispatacher.StartTimer() (available in the DispatcherExtensions class) to create a function that will execute every x seconds/minutes/hours (depending of what you're setting) using the device's clock.
To access the Application's Dispatcher from any class, use the following line:
var dispatcher = Application.Current.Dispatcher;
Since there is no documentation available yet for MAUI, you can read the Device.StartTimer() documentation from Xamarin, which acts exactly the same.

Best practices in dart null safety

I'm currently trying to improve null safety in my flutter app, but having relatively less real-world experiences in working with null safety, I'm not confident in some of my decisions.
For example, my app requires user login, and so I have an Auth class that preserves the state of authentication.
class Auth {
final String? token;
final User? user;
Auth(this.token, this.user);
}
In my app, I ensure that the user property is accessed only when the user is logged in, so it's safe do the following:
final auth = Auth(some_token, some_user);
// and when I need to access the user
final user = auth.user!
which leads to the first question:
Is it recommended to use the null assertion operator in many places in the app?
I personally find it kind of uncomfortable to do things like auth.user!.id throughout the app, so I'm currently handling it like this:
class Auth {
final User? _user;
Auth(this._token, this._user);
User get user {
if (_user == null) {
throw StateError('Invalid user access');
} else {
return _user!;
}
}
}
but I'm not sure if this is a recommended practice in null-safety.
For the next question, I have a class that handles API calls:
class ApiCaller {
final String token;
ApiCaller(this.token);
Future<Data> getDataFromBackend() async {
// some code that requires the token
}
}
// and is accessed through riverpod provider
final apiCallerProvider = Provider<ApiCaller>((ref) {
final auth = ref.watch(authProvider);
return ApiCaller(auth.token);
})
My ApiCaller is accessed through providers and thus the object is created when the App starts. Obviously, it requires token to be available and thus depends on Auth. However, token could also be null when the app starts and the user is not logged in.
Since I'm confident that apiCaller isn't used when there is no existing user, doing this:
class ApiCaller {
// make token nullable
final String? token;
ApiCaller(this.token);
Future<Data> getDataFromBackend() async {
// some code that requires the token
// and use token! in all methods that need it
}
}
final apiCallerProvider = Provider<ApiCaller>((ref) {
final auth = ref.watch(authProvider);
if (auth.token == null) {
return ApiCaller()
} else {
return ApiCaller(auth.token);
}
})
should be fine. However, this also makes me use a lot of token! throughout all methods, and I'm not too sure about that.
I could also simply do ApiCaller('') in the non-null token version, but this seems more of a workaround than a good practice.
Sorry for the lengthy questions. I tried looking for some better articles about real-world practices in null-safety but most are only language basics, so I hope some of you on here could give me some insights. Thanks in advance!
The simplest way to avoid using ! when you know a nullable variable is not null is by making a non-null getter like you did on your first question:
User get user {
if (_user == null) {
throw StateError('Invalid user access');
} else {
return _user!;
}
}
I will let you know that there is no need to check if the value is null before throwing an error, the null check operator does exactly that:
Uset get user => _user!;
Unless of course you care a lot about the error itself and want to throw a different error.
As for your second question, that one is a bit trickier, you know you will not access the variable before it is initialized, but you have to initialize it before it has a value, thus your only option is to make it null, I personally don't like to use the late keyword, but it was built expressly for this purpose, so you could use it. A late variable will not have a value until expressly assigned, and it will throw an error otherwise, another solution is to make a non-null getter like on the other page.
Also, you don't need a null check here because the result is the same:
if (auth.token == null) {
return ApiCaller()
} else {
return ApiCaller(auth.token);
}
instead do this:
return ApiCaller(auth.token);
This does feel to me like a simple problem, you are just not used to working with null-safety, which means that to you, the ! looks ugly or unsafe, but the more you work with it the more you'll become comfortable with it and the less it will look as bad code even if you use it a lot around your app.
Hopefylly, my answer is helpful to you
Is it recommended to use the null assertion operator in many places in the app?
I consider the null assertion operator to be a bit of a code smell and try avoid using it if possible. In many cases, it can be avoided by using a local variable, checking for null, and allowing type promotion to occur or by using null-aware operators for graceful failure.
In some cases, it's simpler and cleaner to use the null assertion as long as you can logically guarantee that the value will not be null. If you're okay with your application crashing from a failed null assertion because that should be logically impossible, then using it is perfectly fine.
I personally find it kind of uncomfortable to do things like
auth.user!.id throughout the app, so I'm currently handling it like
this:
class Auth {
final User? _user;
Auth(this._token, this._user);
User get user {
if (_user == null) {
throw StateError('Invalid user access');
} else {
return _user!;
}
}
}
but I'm not sure if this is a recommended practice in null-safety.
Unless you want to control the error, throwing the StateError is pointless. The null assertion operator will throw an error (a TypeError) anyway.
I personally don't see much value in the user getter. You'd still be using the null assertion operator everywhere, but it'd just be hidden behind a method call. It'd make the code prettier, but it'd be less clear where potential failure points are.
If you find yourself using the null assertion operator for the same variable multiple times in a function, you still can use a local variable to make that nicer:
void printUserDetails(Auth auth) {
final user = auth.user;
user!;
// `user` is now automatically promoted to a non-nullable `User`.
print(user.id);
print(user.name);
print(user.emailAddress);
}
I think ultimately you need to decide what you want your public API to be and what its contracts are. For example, if a user is not logged in, does it make sense to have an Auth object at all? Could you instead have make Auth use non-nullable members, and have consumers use Auth? instead of Auth where null means "not logged in"? While that would be passing the buck to the callers, making them check for null everywhere instead, they're already responsible to not do anything that accesses Auth.user when not logged in.
Another API consideration is what you want the failure mode to be. Does your API contract stipulate in clear documentation that callers must never access Auth.user when not logged in? If the caller is in doubt, are they able to check themselves? If so, then making accesses to Auth.user fatal when it's null is reasonable: the caller violated the contract due to a logical error that should be corrected.
However, in some situations maybe that's too harsh. Maybe your operation can fail at runtime for other reasons anyway. In those cases, you could consider failing gracefully, such as by returning null or some error code to the caller.
final apiCallerProvider = Provider<ApiCaller>((ref) {
final auth = ref.watch(authProvider);
if (auth.token == null) {
return ApiCaller()
} else {
return ApiCaller(auth.token);
}
}
Your ApiCaller class as presented does not have a zero-argument constructor, so that doesn't make sense. If you meant for its constructor to be:
final String? token;
ApiCaller([this.token]);
then there's no difference between ApiCaller() and ApiCaller(null), so you might as well just unconditionally use ApiCaller(auth.token).

Is it possible to feed a model name into a Future as a variable or use a callback in a Future in Flutter?

Background
I understand the concept of callbacks from JS. Being on day #5 of flutter I don't really understand the interplay with callbacks at all in dart/flutter.
So please bear with me
What I'm trying to accomplish
I'm trying to build a single set of reusable code that can communicate from my app to a JSON site to make various different JSON requests via the same exact code base
What I have right now
I have a Future call which successfully polls a site for JSON data and sends it off for parsing by using a specific model.
Future<Result> getJSONfromTheSite(String thecall) async {
try {
//2
final response = await client.request(requestType: RequestType.GET,
path: thecall).timeout(const Duration(seconds: 8));
if (response.statusCode == 200) {
//3
return Result<MyModel>.success(MyModel.fromRawJson(response.body));
} else {
return Result.error(title:"Error",msg:"Status code not 200", errorcode:1);
}
} catch (error) {
(...)
}
}
I'm making the request via my UI page like this:
FutureBuilder(
future: _apiResponse.getJSONfromTheSite('latestnews'),
builder: (BuildContext context, AsyncSnapshot<Result> snapshot) {
if (snapshot.data is SuccessState) { ...
Using the variable thecall which I feed in from the FutureBuilder on my various UI pages I can successfully change which http JSON request is being made to poll for different data.
What I'm stuck on
Where I'm stuck is that while I can successfully vary the request to change what data I'm polling for, I can't do anything with the result because my current code always wants to use MyModel to parse the JSON.
return Result<MyModel>.success(MyModel.fromRawJson(response.body));
The question
I essentially need to be able to switch the Model being used against this JSON data depending on what UI page is making the request, rather then hardcoding 10 versions of the same code.
I was thinking of feeding in the name of the model I want to use for that specific call via the FutureBuilder call. For example something like future: _apiResponse.getJSONfromTheSite('latestnews', MyModel2), but that didn't work at all.
Alternatively I was thinking of having the entire return Result<MyModel>.success(MyModel2.fromRawJson(response.body)); sent in as a callback.
The concept of callback makes sense from my JS days, but I'm not sure if I'm correctly applying the concept here. So if I'm going about this the wrong way I'm all ears to a more elegant solution.
Thank you!
You could simply pass as a callback the constructor you want to use and make your method getJSONfromTheSite dynamically typed. The only issue is that you won't be able to define fromRawJson as a factory constructor but instead as a static method returning an instance of your object.
Code Sample
// Your method fromRawJson should be implemented like this
class MyModel {
// ...
static MyModel fromRawJson(Map<String, dynamic> json) =>
MyModel(/* your parameters */);
}
/// T is the dynamic type which you will use to define if it is
/// MyModel or any other class.
Future<Result<T>> getJSONfromTheSite<T>(
String thecall,
T Function(Map<String, dynamic>) jsonFactory,
) async {
try {
// ...
return Result<T>.success(jsonFactory(response));
} catch (error) {
// ...
}
}
// Then you can call your method like this
await getJSONfromTheSite<MyModel>('latestnews', MyModel.fromRawJson);
// For another class such as MyModel2 it would be like this
await getJSONfromTheSite<MyModel2>('', MyModel2.fromRawJson);
Try the full code on DartPad

Get newly created id of a record before redirecting page

I would like to retrieve the id of a newly created record using javascript when I click on save button and just before redirecting page.
Do you have any idea please ?
Thank you !
One way to do this in Sugar 7 would be by overriding the CreateView.
Here an example of a CustomCreateView that outputs the new id in an alert-message after a new Account was successfully created, but before Sugar gets to react to the created record.
custom/modules/Accounts/clients/base/views/create/create.js:
({
extendsFrom: 'CreateView',
// This initialize function override does nothing except log to console,
// so that you can see that your custom view has been loaded.
// You can remove this function entirely. Sugar will default to CreateView's initialize then.
initialize: function(options) {
this._super('initialize', [options]);
console.log('Custom create view initialized.');
},
// saveModel is the function used to save the new record, let's override it.
// Parameters 'success' and 'error' are functions/callbacks.
// (based on clients/base/views/create/create.js)
saveModel: function(success, error) {
// Let's inject our own code into the success callback.
var custom_success = function() {
// Execute our custom code and forward all callback arguments, in case you want to use them.
this.customCodeOnCreate(arguments)
// Execute the original callback (which will show the message and redirect etc.)
success(arguments);
};
// Make sure that the "this" variable will be set to _this_ view when our custom function is called via callback.
custom_success = _.bind(custom_success , this);
// Let's call the original saveModel with our custom callback.
this._super('saveModel', [custom_success, error]);
},
// our custom code
customCodeOnCreate: function() {
console.log('customCodeOnCreate() called with these arguments:', arguments);
// Retrieve the id of the model.
var new_id = this.model.get('id');
// do something with id
if (!_.isEmpty(new_id)) {
alert('new id: ' + new_id);
}
}
})
I tested this with the Accounts module of Sugar 7.7.2.1, but it should be possible to implement this for all other sidecar modules within Sugar.
However, this will not work for modules in backward-compatibility mode (those with #bwc in their URL).
Note: If the module in question already has its own Base<ModuleName>CreateView, you probably should extend from <ModuleName>CreateView (no Base) instead of from the default CreateView.
Be aware that this code has a small chance of breaking during Sugar upgrades, e.g. if the default CreateView code receives changes in the saveModel function definition.
Also, if you want to do some further reading on extending views, there is an SugarCRM dev blog post about this topic: https://developer.sugarcrm.com/2014/05/28/extending-view-javascript-in-sugarcrm-7/
I resolved this by using logic hook (after save), for your information, I am using Sugar 6.5 no matter the version of suitecrm.
Thank you !

How does the session state work in MVC 2.0?

I have a controller that stores various info (Ie. FormID, QuestionAnswerList, etc). Currently I am storing them in the Controller.Session and it works fine.
I wanted to break out some logic into a separate class (Ie. RulesController), where I could perform certain checks, etc, but when I try and reference the Session there, it is null. It's clear that the Session remains valid only within the context of the specific controller, but what is everyone doing regarding this?
I would imagine this is pretty common, you want to share certain "global" variables within the different controllers, what is best practice?
Here is a portion of my code:
In my BaseController class:
public List<QuestionAnswer> QuestionAnswers
{
get
{
if (Session["QuestionAnswers"] == null)
{
List<QuestionAnswer> qAnswers = qaRepository.GetQuestionAnswers(CurrentSection, UserSmartFormID);
Session["QuestionAnswers"] = qAnswers;
return qAnswers;
}
else
{
return (List<QuestionAnswer>)Session["QuestionAnswers"];
}
}
set
{
Session["QuestionAnswers"] = value;
}
}
In my first Controller (derived from BaseController):
QuestionAnswers = qaRepository.GetQuestionAnswers(CurrentSection, UserSmartFormID);
I stepped through the code and the above statement executes fine, setting the Session["QuestionAnswers"], but then when I try to get from another controller below, the Session["QuestionAnswers"] is null!
My second controller (also derived from BaseController):
List<QuestionAnswer> currentList = (List<QuestionAnswer>)QuestionAnswers;
The above line fails! It looks like the Session object itself is null (not just Session["QuestionAnswers"])
does it make a difference if you retrieve your session using
HttpContext.Current.Session("mySpecialSession") ''# note this is VB, not C#
I believe TempData will solve your problem, it operates with in the session and persists across multiple requests, however by default it will clear the stored data once you access it again, if that's a problem you can tell it to keep the info with the newly added Keep() function.
So in your case:
...
TempData["QuestionAnswers"] = qAnswers;
...
There's much more info at:
http://weblogs.asp.net/jacqueseloff/archive/2009/11/17/tempdata-improvements.aspx
Where are you accessing the session in the second controller? The session object is not available in the constructor because it is injected later on in the lifecycle.
Ok, finally got it working, although a bit kludgy. I found the solution from another related SO post.
I added the following to my BaseController:
public new HttpContextBase HttpContext
{
get
{
HttpContextWrapper context =
new HttpContextWrapper(System.Web.HttpContext.Current);
return (HttpContextBase)context;
}
}
Then set/retrieved my Session variables using HttpContext.Session and works fine!