"#define" equivalent in Dart/Flutter? - flutter

I wonder if there is a way to #define short_expression long_expression in Dart/Flutter?
For example, instead of typing
MediaQuery.of(context).size.width
// or
Locale.of(context).translate("x")
in every build function, we can
#define MQWidth MediaQuery.of(context).size.width
// or
#define Lt(x) Locale.of(context).translate("x")
then use it in every build function instead?

Macros. Generative programming aka metaprogramming is usually not a thing in interpreted languages. The closest thing to generative programming concept in dart would be static metaprogramming that is actively being chased by. You can currently achieve source code generation in dart via build_runner, which I believe you have seen for example in packages such as json_serializable, and retrofit. But it's still way too far from perfect and requires a lot of work to achieve something even minuscule. And it only fits into certain scenarios and the example you gave is sadly not one of them.
So, the answer is no. You can't do that, at least as of now.
But instead why don't you just separate that into functions if they are really overused all over the place. I know you still have to pass in the context everywhere which is kinda cluttering compared to what you have expected. But it'll still make the code much shorter, if that's your goal.
Hope this answers your question. Cheers!

An example of how this can be done.
By the way, this is the simplest example. Without using of the macro arguments (AST nodes).
Input: bin/main.dart
#pragma('meta_expression:build')
library my_cool_library;
#pragma('meta_expression:import')
import 'package:test_meta_expression/my_cool_macros.dart';
void main(List<String> args) {
final a = mqWidth();
final b = lt('Hello!');
print(a);
print(b);
}
Output: bin/main.impl.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// MetaExpressionLibraryGenerator
// **************************************************************************
library my_cool_library;
void main(List<String> args) {
//
final a = MediaQuery.of(context).size.width;
final b = Locale.of(context).translate('Hello!');
print(a);
print(b);
}
Macro: package:test_meta_expression/my_cool_macros.dart
import 'package:meta_expression_annotation/meta_expression_annotation.dart';
#MetaExpression(mqWidthImpl)
external int mqWidth();
String mqWidthImpl(MetaContext context) => '''
MediaQuery.of(context).size.width
''';
#MetaExpression(ltImpl)
external String lt(String x);
String ltImpl(MetaContext context) => '''
Locale.of(context).translate(x)
''';
File: pubspec.yaml
dependencies:
meta_expression_annotation: 0.2.1
dev_dependencies:
build_runner: any
meta_expression: 0.2.1
Build command:
dart run build_runner build

Related

Getting learning_text_recognition library to work in Flutter

I'm trying to add the learning_text_recognition library to my Flutter project. I was able to get the example in the API docs to work with no problems (https://pub.dev/packages/learning_text_recognition/example), but now I'm trying to add it to my own project using the information found on the Readme tab of the same website. It's slightly different than how the example worked and I'm now receiving several errors that I didn't receive in the example. Specifically, the errors are on the following line:
RecognizedText result = await textRecognition.process(image);
It says that the await function can only be used in an async function, but I don't know if I should make the function or the class async? It also says that the method 'process isn't defined for the type 'TextRecognition', but I don't know what the method should be, since that part worked perfectly fine in the example. It was also complaining that image wasn't defined, but I just created a variable called image with InputCameraView, which seemed to work.
I've tried moving the code into a new function and made an image variable. This is what the code looks like now:
getInfo(){
var image = InputCameraView(
canSwitchMode: false,
mode: InputCameraMode.gallery,
title: 'Text Recognition',
onImage: (InputImage image) {
// now we can feed the input image into text recognition process
},
);
TextRecognition textRecognition = TextRecognition();
RecognizedText result = await textRecognition.process(image);
}
I've also included the following import statements:
import 'package:learning_input_image/learning_input_image.dart';
import 'package:learning_text_recognition/learning_text_recognition.dart';
import 'package:provider/provider.dart';
I'm not sure if I'm maybe missing a step?
Your function should have the async keyword to indicate that there will be wait points. See the dart async/await documentation.
Another detail for the example InputCameraView is a widget, it should not be inside the function. It must be using the onImage method of the InputCameraView to collect the recognition and the builder to build it. In the doc onImage calls the async function _startRecognition to collect the data you must do something in this line.
void getInfo() async {
var image = InputCameraView(
canSwitchMode: false,
mode: InputCameraMode.gallery,
title: 'Text Recognition',
onImage: (InputImage image) {
// now we can feed the input image into text recognition process
},
);
var textRecognition = TextRecognition();
var result = await textRecognition.process(image);
}

Null check operator error with tflite Flutter

The following code causes this error Null check operator used on a null value but I cannot figure out why.
import 'package:tflite_flutter/tflite_flutter.dart';
final modelFile = 'model.tflite';
void main() async {
foo();
}
void foo() async {
Interpreter inter = await Interpreter.fromAsset(modelFile);
}
The model exists at the correct location and I have tried multiple other locations just in case. The file is in my pubspec.yaml and I have tried multiple variations just in case. Not sure if I need to provide more parameters or something but I can't figure it out. Any help would be appreciated.
Just to test if it was an issue with me not placing the file in the correct location, or not referencing it in the pubspec.yaml I tested other assets such as Images and Text files in the same location and they loaded perfectly fine.
It looks like you have not exported the file. To fix to go to pubspec.yaml and find these lines:
flutter:
assets:
- model.tflite
And make sure you are specifying the correct file location when adding it through pubscpec. After that uninstall the app, do flutter clean, flutter pub get and it should work
I assumer the directory containing the model is named as "assets".
import 'package:tflite_flutter/tflite_flutter.dart';
final modelFile = 'assets/model.tflite';
void main() async {
foo();
}
void foo() async {
Interpreter inter = await Interpreter.fromAsset(modelFile);
}
Code for pubspec:-
flutter:
assets:
- assets/model.tflite

Flutter Riverpod design pattern (inhibit garbage collection)

I've written a Swift/IOS package to externalize and standardize all of my Social/Federated/Firebase authentication boilerplate (both SDK's and UI). I've taken it upon myself to port this to Flutter as a learning exercise ... but to also allow custom UI to be passed-in via config.
Since I'm new to Flutter & Riverpod, I'm afraid I'm making some serious mistake & want to get feedback from you experts before I go too deep.
The package is called "Social Login Helper" or SLH, and this is the public API I desire:
runApp(
slh.authStateBuilder(
builder: (authStatus) {
switch (authStatus.stage) {
case SlhResultStage.initializing:
return SplashScreen();
case SlhResultStage.unauthenticated:
// using Riverpod and Nav 2.0
return slh.authFlowUi;
case SlhResultStage.authenticated:
return ExampleApp(appKey, authStatus, slh.logoutCallback);
case SlhResultStage.wantsAnnonOnlyFeatures:
return ExampleApp(appKey, null, slh.startAuthCallback);
case SlhResultStage.excessiveFailures: // restart the app
return TotalFailure();
}
},
),
);
As you can see from the above, the State/Stream builder at root must never be garbage collected or purged. I'm unclear if and when Riverpod will dispose my provider, or if Dart itself will collect objects that must remain immortal. I'm also unsure whether to use a StreamProvider or a State provider??
As you can see below, I've created an intentional memory-leak (deadlock) to guard me. I'm sure it's an anti-pattern, but being novice, I'm not sure how else to guarantee immortality.
All guidance and explicit feedback would be most welcome.
class LivingAuthState extends StateNotifier<SlhResultStage> {
// create deadly embrace to prevent this from ever being collected
_Unpurgeable _up;
LivingAuthState() : super(SlhResultStage.initializing) {
//
final StreamProvider<SlhResultStage> rssp =
StreamProvider<SlhResultStage>((ref) {
return this.stream.asBroadcastStream();
});
_up = _Unpurgeable(this, rssp);
// how do I keep rssp from ever being collected??
}
StreamProvider<SlhResultStage> get authStatusStream => _up.rssp;
void logout() {
this.state = SlhResultStage.unauthenticated;
}
void restartLogin() {
this.state = SlhResultStage.unauthenticated;
}
}
class _Unpurgeable {
final LivingAuthState _aliveState;
final StreamProvider<SlhResultStage> rssp;
_Unpurgeable(this._aliveState, this.rssp);
}
One improvement I'd like to see in the Riverpod documentation is clarity on HOW LONG a provider will live, WITHOUT an active listener, before it will self-dispose / garbage-collect.
Ah, it looks like I can subclass AlwaysAliveProviderBase() to achieve the same goal ... I'll experiment with this.
Move your provider final to the top level. Riverpod providers are top-level final variables.
Also remember to wrap your app in the riverpod provider.

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

How can I check if a Flutter application is running in debug?

I'm looking for a way to execute code in Flutter when the app is in Debug mode. Is that possible in Flutter? I can't seem to find it anywhere in the documentation.
Something like this
If(app.inDebugMode) {
print("Print only in debug mode");
}
How can I check if the Flutter application is running in debug or release mode?
In later versions, you can use kDebugMode:
if (kDebugMode)
doSomething();
While asserts can technically be used to manually create an "is debug mode" variable, you should avoid that.
Instead, use the constant kReleaseMode from package:flutter/foundation.dart
The difference is all about tree shaking.
Tree shaking (aka the compiler removing unused code) depends on variables being constants.
The issue is, with asserts our isInReleaseMode boolean is not a constant. So when shipping our app, both the dev and release code are included.
On the other hand, kReleaseMode is a constant. Therefore the compiler is correctly able to remove unused code, and we can safely do:
if (kReleaseMode) {
} else {
// Will be tree-shaked on release builds.
}
Here is a simple solution to this:
import 'package:flutter/foundation.dart';
Then you can use kReleaseMode like
if(kReleaseMode){ // Is Release Mode??
print('release mode');
} else {
print('debug mode');
}
Please use Remi's answer with kReleaseMode and kDebugMode or Dart compilation won't be able to tree-shake your code.
This little snippet should do what you need:
bool get isInDebugMode {
bool inDebugMode = false;
assert(inDebugMode = true);
return inDebugMode;
}
If not, you can configure your IDE to launch a different main.dart in debug mode where you can set a Boolean.
kDebugMode
You can now use the kDebugMode constant.
if (kDebugMode) {
// Code here will only be included in debug mode.
// As kDebugMode is a constant, the tree shaker
// will remove the code entirely from compiled code.
} else {
}
This is preferable over !kReleaseMode as it also checks for profile mode, i.e., kDebugMode means not in release mode and not in profile mode.
kReleaseMode
If you just want to check for release mode and not for profile mode, you can use kReleaseMode instead:
if (kReleaseMode) {
// Code here will only be run in release mode.
// As kReleaseMode is a constant, the tree shaker
// will remove the code entirely from other builds.
} else {
}
kProfileMode
If you just want to check for profile mode and not for release mode, you can use kProfileMode instead:
if (kProfileMode) {
// Code here will only be run in release mode.
// As kProfileMode is a constant, the tree shaker
// will remove the code entirely from other builds.
} else {
}
While this works, using constants kReleaseMode or kDebugMode is preferable. See Rémi's answer below for a full explanation, which should probably be the accepted question.
The easiest way is to use assert as it only runs in debug mode.
Here's an example from Flutter's Navigator source code:
assert(() {
if (navigator == null && !nullOk) {
throw new FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'The context used to push or pop routes from the Navigator must be that of a '
'widget that is a descendant of a Navigator widget.'
);
}
return true;
}());
Note in particular the () at the end of the call - assert can only operate on a Boolean, so just passing in a function doesn't work.
Not to be picky, but the foundation package includes a kDebugMode constant.
So:
import 'package:flutter/foundation.dart' as Foundation;
if(Foundation.kDebugMode) {
print("App in debug mode");
}
I believe the latest way to do this is:
const bool prod = const bool.fromEnvironment('dart.vm.product');
src
Just import this
import 'package:flutter/foundation.dart'
String bulid = kReleaseMode ? "Release" : "";
or
String bulid = kDebugMode ? "Debug" : "";
or
String bulid = kProfileMode ? "Profile" : "";
Or try this
if (kDebugMode) {
print("Debug");
} else if (kReleaseMode) {
print("Release");
} else if (kProfileMode) {
print("Profile");
}
Make a file named constants.dart. Add these variables in it:
const bool kReleaseMode = bool.fromEnvironment('dart.vm.product');
const bool kProfileMode = bool.fromEnvironment('dart.vm.profile');
const bool kDebugMode = !kReleaseMode && !kProfileMode;
printk(String string) {
if (kDebugMode) {
// ignore: avoid_print
print(string);
}
}
Then import this constant file in any other file and use it like this:
import 'package:package_name/constants.dart';
if(kDebugMode){
//Debug code
}else{
//Non-Debug code
}
printk("Debug Log");
I've created this useful class, based on other answers and inspired on Android usage.
If anything changes on "Foundation" package, it would not be necessary to change the entire application, it would be necessary to change only this class.
import 'package:flutter/foundation.dart' as Foundation;
abstract class Build {
static const bool isDebugMode = Foundation.kDebugMode;
static const bool isReleaseMode = Foundation.kReleaseMode;
static const bool isWeb = Foundation.kIsWeb;
static const bool isProfileMode = Foundation.kProfileMode;
}
Extracted from Dart Documentation:
When exactly do assertions work? That depends on the tools and
framework you’re using:
Flutter enables assertions in debug mode.
Development-only tools such as dartdevc typically enable assertions by default.
Some tools, such as dart and dart2js, support assertions through a command-line flag: --enable-asserts.
In production code, assertions are ignored, and the arguments to
assert aren’t evaluated.