How to create global variables using Provider Package in Flutter? - flutter

My Flutter app needs a global variable that is not displayed(so no UI changes) but it needs to run a function everytime it is changed. I've been looking through tutorials etc. but they all seem to be for much more complicated uses than what I need and I'd prefer to use the simplest approach that is still considered "good practice".
Roughly what I am trying to do:
//inside main.dart
int anInteger = 0;
int changeInteger (int i) = {
anInteger = i;
callThisFunction();
}
//inside another file
changeInteger(9);

You can make a new Class in a new file to store the global variable and its related methods. Whenever you want to use this variable, you need to import this file. The global variable and its related methods need to be static. Pay attention to the callThisFunction that you mentioned in your question, it needs to be static as well (since it would be called in a static context). e.g.
file: globals.dart
class Globals {
static var anInteger = 0;
static printInteger() {
print(anInteger);
}
static changeInteger(int a) {
anInteger = a;
printInteger(); // this can be replaced with any static method
}
}
file: main.dart
import 'globals.dart';
...
FlatButton(
onPressed: () {
Globals.changeInteger(9);
},
...

Related

how to add and select color for nodes/tree view items in explorer view in my vscode extension

I have added my own explorer view in my extension.
Here I added nodes/tree view items however I am not finding any way to customize and choose color my tree view items in explorer view.
Any idea how to achieve this?
There should be some way because when some file has error then its color is set to different compared to other open file.
[I assume this is your github issue: Not able to use FileDecorationProvider for tree view item.]
Here is my attempt at using a FileDecorationProvider for a custom TreeView. With the caveat that I am new to typescript and FileDecorations.
If you have seen Support proposed DecorationProvider api on custom views you know there are limitations on using a FileDecorationProvider for coloring TreeItem's - primarily that the decoration/coloration cannot be limited to your treeView - wherever that resourceUri apeears, like in the Explorer, your fileDecoration will be applied. That is very unfortunate but I don't believe there is any way to avoid that for now.
First, in your TreeItem class you will have to give whichever items you want decorated a resourceUri. Like this:
export class TreeTab extends vscode.TreeItem {
constructor( public readonly tab: vscode.Tab, public index: number = 0 ) {
super(tab.label, vscode.TreeItemCollapsibleState.None);
this.tab = tab;
if (tab.input instanceof vscode.TabInputText) {
this.resourceUri = tab.input.uri;
}
}
Ignore the specifics of the code for my extension, the point is:
this.resourceUri = <some vscode.Uri>;
Secondly, this is how I set up my FileDecoration class:
import {window, Tab, TabInputText, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor} from 'vscode';
export class TreeFileDecorationProvider implements FileDecorationProvider {
private disposables: Array<Disposable> = [];
private readonly _onDidChangeFileDecorations: EventEmitter<Uri | Uri[]> = new EventEmitter< Uri | Uri[]>();
readonly onDidChangeFileDecorations: Event<Uri | Uri[]> = this._onDidChangeFileDecorations.event;
constructor() {
this.disposables = [];
this.disposables.push(window.registerFileDecorationProvider(this));
}
async updateActiveEditor(activeTab: Tab): Promise<void> {
if (activeTab.input instanceof TabInputText)
this._onDidChangeFileDecorations.fire(activeTab.input.uri);
// filter to get only non-activeTabs
activeTab.group.tabs.map( tab => {
if (!tab.isActive && tab.input instanceof TabInputText)
this._onDidChangeFileDecorations.fire(tab.input.uri);
});
}
async provideFileDecoration(uri: Uri): Promise<FileDecoration | undefined> {
const activeEditor = window.activeTextEditor.document.uri;
if (uri.fsPath === activeEditor.fsPath) {
return {
badge: "⇐",
color: new ThemeColor("charts.red"),
// color: new vscode.ThemeColor("tab.activeBackground"),
// tooltip: ""
};
}
else return null; // to get rid of the custom fileDecoration
}
dispose() {
this.disposables.forEach((d) => d.dispose());
}
}
provideFileDecoration(uri: Uri) does the actual decorating. It finds only certain files and decorates them, and by returning null resets that previously decorated uri (as supplied by the uri argument).
updateActiveEditor() is an exported method that I call in other parts of the extension when I want to change a file decoration. So elsewhere I have this in another file:
import { TreeFileDecorationProvider } from './fileDecorator';
export class EditorManager {
public TreeItemDecorator: TreeFileDecorationProvider;
// and then on a listener that gets triggered when I need to make a change to some things including the FileDecoration for a uri
this.TreeItemDecorator.updateActiveEditor(activeTab);
this.TreeItemDecorator.updateActiveEditor(activeTab); that calls the updateActiveEditor method in the TreeFileDecorationProvider class which calls the this._onDidChangeFileDecorations.fire(<some uri>); method for uri's that need to have the decoration applied and also for uri's that need to have the decoration removed.
this._onDidChangeFileDecorations.fire(<some uri>); will call provideFileDecoration(uri: Uri) where the actual decoration will be applied or removed depending on some state of that uri.
I am sure there is a way to call onDidChangeFileDecorations() directly from another file in your project (if you don't need to do any pre-processing of the uri like I have to do. I just haven't figured out how to construct the argument for that function yet. Perhaps someone will help on that point.
You can see here:
color: new ThemeColor("charts.red"),
// color: new vscode.ThemeColor("tab.activeBackground"),
how a color is chosen - it must be some ThemeColor. The charts theme colors has a few basic colors that are handy to refer to. See theme color references, Charts therein.
The badge option can take up to 2 characters, but as you see I copied/pasted a unicode character for mine and that works.
As I mentioned my FileDecorationProvider is called from an eventListener, but you may not need that for your use case - if decorations do not have to added and removed based on user actions like in my case. So you may be able to call your FileDecorationProvider right from your extension.ts activate() like so:
import * as vscode from 'vscode';
import { TreeFileDecorationProvider } from './fileDecorator';
export async function activate(context: vscode.ExtensionContext) {
new TreeFileDecorationProvider();
}
Other references:
a treeDecorationProvider.ts example
part of the git extension that does file decorations
Custom view decorations in VSCode extension

GetX Unbind Stream

I am using the bindStream() function with the GetX package inside a controller.
class FrediUserController extends GetxController {
#override
void onReady() {
super.onReady();
final userController = Get.find<FrediUserController>();
var groupIds = userController.user.groups;
groupList.bindStream(DatabaseManager().groupsStream(groupIds));
ever(groupList, everCallback);
}
}
But, when the groupIds update in the FrediUserController (with an ever function that gets triggered, I want to RE-bind the streams. Meaning, delete the existing ones and bind again with new ids, or replace the ones that have changed.
Temporary Solution: Inside ever() function
Get.delete<FrediGroupController>();
Get.put(FrediGroupController());
This code gets run everytime my groupIds change from the database. But I do not want to initiate my controllers every time a small thing changes, it is bad UX.
This seems difficult, could someone guide me to the right direction? Maybe there is a completely different approach to connecting two GetX controllers?
Note: the first one include editing the source code of the Getx package.
first:
looking in the source code of the package :
void bindStream(Stream<T> stream) {
final listSubscriptions =
_subscriptions[subject] ??= <StreamSubscription>[];
listSubscriptions.add(stream.listen((va) => value = va));
}
here is what the bind stream actually do, so if we want to access the listSubscriptions list, I would do:
final listSubscriptions;
void bindStream(Stream<T> stream) {
listSubscriptions =
_subscriptions[subject] ??= <StreamSubscription>[];
listSubscriptions.add(stream.listen((va) => value = va));
}
now from your controller you will be able to cancel the streamSubscription stored in that list with the cancel method like this :
listSubscriptions[hereIndexOfThatSubscription].cancel();
then you can re-register it again with another bindStream call
second :
I believe also I've seen a method called close() for the Rx<T> that close the subscriptions put on it, but I don't know if it will help or not
Rx<String> text = ''.obs;
text.close();
I've also run into this issue, and there appears to be no exposed close function. There is a different way to do it though, using rxdart:
import 'package:get/get.dart';
import 'package:rxdart/rxdart.dart' hide Rx;
class YourController extends GetxController {
final value = 0.obs;
final _closed = false.obs;
void bindValue(Stream<int> valueStream) {
_closed
..value = true
..value = false;
final hasClosed = _closed.stream.where((c) => c).take(1);
value.bindStream(
valueStream.takeUntil(hasClosed)
);
}
}
Whenever you want to unbind, just set _closed.value = true.

Flutter : How to use setter to reset value

I have variable called to totalQuantity in provider:
get totalQuantity => total_quantity();
total_quantity() {
var totalQty = 0;
for (var x in myCart) {
totalQty += (x.quantity);
}
return totalQty;
}
I use it in the app bar:
child: Text('${prod.totalQuantity}',
I have a logout function I want when I pressed on it to reset totalQuantity, I guess using setter for that in provider, but I don't know how to do that.
IconButton(
onPressed: () {
prod.clear_myCart();
loginProd.log_out();
// ----------------- I want to reset it here
},
I found my mistake ,I forgot to add listen notifier
void clear_myCart() {
myCart.clear();
notifyListeners();
}
after I add it ,it works fine
I understand that you want to return totalQuantity to the original (empty) value, so lets have a look at where it gets its value from:
Your total_quantity() function depends on one variable, myCart.
So, if you clear myCart in prod.clear_myCart();, the quantity should also be updated accordingly.
Now, what your code does not show is how the value change of myCart is being handled in your code;
I am speculating here because your code snippets don't provide enough information, but your ChangeNotifier might just not call notifyListeners() when you call prod.clear_myCart(); (See https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple).

Is a constant variable more performant when switching between widgets and if so how do you achieve this at the entry point of app?

I try to use the same code for both web and android. Where the code differs I switch between widgets based on a global variable.
Is the performance worse when using a non constant / non final variable when switching between widgets? I'm thinking, because the variable is not final or constant and can be changed at any point, Flutter will not be able to optimise the code. Is that true? If inefficient, how do I make my code efficient?
eg.
I have two main files and set my AppType enum in each
[appType.dart]
AppType appType; //can't think of how to make this constant or final
[android_main.dart]
void main() {
appType = AppType.and;
[web_main.dart]
void main() {
appType = AppType.and;
In my widgets I switch where I need a widget specific for the web or android
if(appType == AppType.web)
return MyWidgetWeb();
else
return MyWeigetAnd();
Yes, a constant is more efficient, mainly because of tree-shaking.
Assume that you have the following types:
enum AppType {
mobile,
web,
}
class Mobile {}
class Web {}
Then when you write:
const type = AppType.web;
void main() {
if (type == AppType.web) {
print(Web());
}
else if (type == AppType.mobile) {
print(Mobile());
}
}
Then when compiling the code, the compiler knows that the if block will always be reached, and the else if never will.
As such:
the conditions are removed. When compiled, the code will be:
const type = AppType.web;
void main() {
// no `if` performed
print(Web());
}
Mobile will not be bundled in the executable, so you have a lighter application.
To fully benefit from this behavior, you can use Dart "defines", using int/bool/String.fromEnvironment, which allows you to define constants that behave differently depending on some external build parameters.
The way such constant would look like is:
const isWeb = bool.fromEnvironment('isWeb', defaultValue: false);
Which you can then control using arguments on flutter run and flutter build commands:
flutter build <whatever> --dart-define=isWeb=true

Writing SAPUI5 control renderer in "sap.ui.define" style

I'd like to write custom control in a new sap.ui.define fashion. I have a problem implementing control renderer in a separate file: it seems one have to put bExport = true, while this is forbidden by SAP.
bExport: whether an export to global names is required - should be used by SAP-owned code only
I haven't found any examples of renderer implementations that doesn't utilize export hack and I have a doubt if such a way ever exists.
I've got some suggestions, but they doesn't fully satisfy me:
Ignore SAP requirement and use bExport = true. Pros: highest reuse of SAP code and generally follow standard logic. Cons: avoiding official recommendations.
Explicitly set my.namespace.control.GreatControlRenderer from within factory function. Pros: simple, doesn't touch bExport. Cons: breaks modular design (as module actually sets global variable).
What is the best or recommended way to resolve this issue?
Technically, an object reference available for public use is created inside framework code with jQuery.sap.setObject method, both in:
sap.ui.core.Control.extend() -- actually within parent class method
sap.ui.base.Metadata.createClass()
sap.ui.define(/* bExport = */ true)
This method creates hierarchy of objects in global scope by object dot.separated.qualified.name as follows:
jQuery.sap.setObject = function(sName, vValue, oContext) {
var oObject = oContext || window,
aNames = (sName || "").split("."),
l = aNames.length,
i;
if (l > 0) {
for (i = 0; oObject && i < l - 1; i++) {
if (!oObject[aNames[i]]) {
oObject[aNames[i]] = {};
}
oObject = oObject[aNames[i]];
}
oObject[aNames[l - 1]] = vValue;
}
};
First, your Renderer is an Object with a render function. It's a static function. Return this Renderer from your module.
sap.ui.define([], function(){
"use strict";
var MyControlRenderer = {};
MyControlRenderer.apiVersion = 2;
MyControlRenderer.render = function(oRm, oControl){
// Render your control
};
return MyControlRenderer;
});
Then in your control, you import your Renderer object and assign it to the renderer property of your control like this:
sap.ui.define([
"sap/ui/core/Control",
"./MyControlRenderer"
], function(Control, MyControlRenderer) {
"use strict";
var MyControl = Control.extend("bla.MyControl", {
metadata: {
// ...
},
renderer: MyControlRenderer
});
return MyControl;
});
In this example the renderer is in the same directory as the control.