Read variable from MainActivity.java in Flutter Dart - android

I want to disable logging of Firebase Analytics in a Flutter project when the app is being run on Firebase Test Lab. According to Firebase docs, TestLab can be detected by adding the following in MainActivity.java
String testLabSetting = Settings.System.getString(getContentResolver(), "firebase.test.lab");
if ("true".equals(testLabSetting)) {
// Do something when running in Test Lab
// ...
}
How can I access the result of this test on the dart side in main.dart which is where I want to disable logging (as there are some other reasons logging is disabled already in the dart code).
Thanks!

I just found this. I didn't try it yet though:
https://pub.dev/packages/flutter_runtime_env
This project allows you to check if you're running in the Firebase
Test Labs
You can use it like their example:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_runtime_env/flutter_runtime_env.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _shouldBeEnabled = false;
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
var result = await shouldEnableAnalytics();
setState(() {
_shouldBeEnabled = result;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Should Enable Analytics'),
),
body: Center(
child: Text('Should Analytics be Enabled: $_shouldBeEnabled\n'),
),
),
);
}
}
EDIT:
I think I found a better solution.
https://pub.dev/packages/flutter_sentry
It has the follow method
/// Return `true` if running under Firebase Test Lab (includes pre-launch
/// report environment) on Android, `false` otherwise.
static Future<bool> isFirebaseTestLab() async
It seems to be the best solution so far...
EDIT 2:
Fuck it! I just created a small plugin.
https://pub.dev/packages/is_firebase_test_lab_activated

Related

My flutter property has not assign value in future block

My flutter property has not changed in future blocks. I tried setState(() {}) but not working.
I uninstalled flutterSdk and android SDK then I installed this SDK but still has not changed.
I had the same situation in my previous project. I copied the code of other pages and pasted it to the page that is not working. The page is working a little then page is wrong
this is my code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class apiPage extends StatefulWidget {
const apiPage({Key? key}) : super(key: key);
#override
State<apiPage> createState() => _apiPageState();
}
class _apiPageState extends State<apiPage> {
String _response="exe";//this property is not working
Future <void> ApiCall() async{
String adres="www.api";
http.Response cevap=await http.get(Uri.parse(adres));
if(cevap.statusCode==200){
_response=cevap.body;//property needs to be changed
}else{
_response="error";//feature needs to be changed
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Text('$_response'),
]
),
);
}
}
When you like to update UI with new value, call setState on widget-state class. Here you can do,
if(cevap.statusCode==200){
setState((){
_response=cevap.body;
});
}else{
setState((){
_response="error";
});
}
Make sure to call the ApiCall() method.
A better choice will be using FutureBuilder for future method. Find more about FutureBuilder and StatefulWidget.

Why my flutter http network calls are slow?

I am developing a flutter application with network activities. To get data, I am connecting to a REST API, this API is fast as it should.
For more information, this API is using AWS API Gateway and AWS Lambda along with other AWS technologies.
Below is my code, connecting to network.
class RoleService with ChangeNotifier {
NavLinks _navLinks = NavLinks();
late List<Role> _roles;
/// Get all Roles
Future<void> getAllRoles(String authToken) async {
try {
var data = await http.get(
Uri.parse("https://api2.example.com/userrel/roles/getall"),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
);
var jsonData =
convert.json.decode(data.body).cast<Map<String, dynamic>>();
_roles = jsonData.map<Role>((json) => new Role.fromJson(json)).toList();
print(_roles);
} catch (error) {
print(error);
throw error;
}
}
}
You can see the postman performance of the above API call below. For flutter testing, i am using Huawei p30 Lite android phone.
Then, when I execute the same API call in flutter, this is what I get.
Observing the outputs from postman I can see it has cached the DNS Lookup, TCP Handshake and SSL Handshake. postman does this after calling the API base URI for the first time. Then from the 2nd time onwards, the DNS Lookup etc are cached saving lot of time in future API calls to the same base URI.
But in flutter the "Connection established" time is high, even though the time to retrieve data is only few milliseconds.
How can I avoid the connection delays and get the maximum performance? If caching the SSL, DNS Lookup etc is the solution, how can I do that in flutter?
It seems this question is there for many people. So, let me answer my own question.
Can flutter remember the network connection? Yes it can.
Flutter only require one network call to the same API to remember the connection. From the second call onward to the same API, it will use its "cached" memory giving you a big performance boost.
So first remember, this only works if you are calling the same API multiple times. If you are calling different APIs, this will not work. However in many apps, you have an API that built by the API team and you will be calling the same throughput the app.
The solution is to use flutter http.Client. Then share the same http.Client across the calls you make to the same API. You will see only first call takes time for "connection", rest of the calls do not take that time.
An example is available in flutter http pub page. It says ,
If you're making multiple requests to the same server, you can keep
open a persistent connection by using a Client rather than making
one-off requests. If you do this, make sure to close the client when
you're done:
Check below example. It is only for your reference, not the best way of using this.
main.dart
import 'package:flutter/material.dart';
import 'package:network_test/role_service.dart';
import 'package:network_test/user_role_service.dart';
import 'package:network_test/user_service.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var startTime = "";
var endTime = "";
void _network() async {
var client = http.Client();
RoleService _roleService = RoleService();
UserService _userService = UserService();
UserRoleService _userRoleService = UserRoleService();
String authToken = "****";
String uid = "555555";
try {
await _roleService.getAllRoles(authToken, client);
//await _roleService.getAllRoles(authToken, client);
await _userService.getUserByUID(authToken, uid, client);
await _userService.getUserByID(authToken, 27, client);
await _userRoleService.getUserRoleByUser(authToken, 27, client);
} finally {
client.close();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
"Start Time: " + startTime,
style: Theme.of(context).textTheme.headline4,
),
Text(
"End Time: " + endTime,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _network,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
role_service.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:network_test/role.dart';
import 'dart:convert' as convert;
import 'dart:io';
class RoleService with ChangeNotifier {
late List<Role> _roles;
String link2 = "https://api2.somewhere.com/userrel";
/// Return roles
List<Role> returnRoles() {
return _roles;
}
/// Get all Roles
Future<void> getAllRoles(String authToken, Client client) async {
try {
var data = await client.get(Uri.parse(link2 + "/role/getall"),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"});
var jsonData =
convert.json.decode(data.body).cast<Map<String, dynamic>>();
_roles = jsonData.map<Role>((json) => Role.fromJson(json)).toList();
print(_roles[0].roleName);
} catch (error) {
print(error);
throw error;
}
}
}
now I told you that above is not the best practice. Why? Because you will be creating and destroying the http.Client in many different places. Let's pay attention to a better practice.
In almost every app, we use State Management. I am a fan of Provider, it could be anything of your choice. i figured out the best way is to let the state management to remember the creation of http.Client. Since I am using Provider, I created the following class.
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class ConnectionService with ChangeNotifier {
http.Client _client = http.Client();
http.Client returnConnection() {
return _client;
}
}
And this is my main class
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ConnectionService()),
],
child: MyApp(),
));
}
Now when the app opens, I call the ConnectionService class to make the connection and do my API calls such as checking user authentication, user access etc. And only the first call is taking its time to build the connection, other calls do not.

Flutter - Jitsi Meet Call backs in inAppWebView

I need to integrate Jitsi Meet in webview for our flutter application. Initially I used the following jitsi-meet plugin "https://pub.dev/packages/jitsi_meet" but unfortunately had to switch to InAppwebview plugin "https://pub.dev/packages/flutter_inappwebview" because of missing features such as jitsi reconnection after internet drop and participant information in jitsi-meet plugin. I have successfully integrated the jitsi in webview but have no idea how to include jitsi callbacks like onConferenceJoined, onParticipantLeft etc. Any help would be much appreciated
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Permission.camera.request();
await Permission.microphone.request();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(home: InAppWebViewPage());
}
}
class InAppWebViewPage extends StatefulWidget {
#override
_InAppWebViewPageState createState() => new _InAppWebViewPageState();
}
class _InAppWebViewPageState extends State<InAppWebViewPage> {
InAppWebViewController _webViewController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("InAppWebView")),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://meet.jit.si/hello",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
mediaPlaybackRequiresUserGesture: false,
debuggingEnabled: true,
),
),
onWebViewCreated: (InAppWebViewController controller) {
_webViewController = controller;
},
androidOnPermissionRequest:
(InAppWebViewController controller, String origin,
List<String> resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT);
}),
),
),
])));
}
}
Sorry to say, but you can't access such features through webview, because when you access thorough webview it's similar to open the site on the browser as a user. You can use Jitsi SDK or plugins which allow you to modify the settings.
Suggestion: Use the jitsi meet plugin which you had used before it already has such features as which you want:
onConferenceWillJoin Meeting is loading.
onConferenceJoined User has joined meeting.
onConferenceTerminated User has exited the conference.
onPictureInPictureWillEnter User entered PIP mode.
onPictureInPictureTerminated User exited PIP mode.
onError Error has occurred with listening to meeting events.
But there is no feature for participant information. But you can achieve this all by hosting your own jitsi server, which would allow you to either customize it or directly have such settings on your custom domain so that on the app you can simply access through webview.
I have used this plugin for a long time and made an app published on the play store. Check that out do you something similar to that? app name Just Meet. If you want like that then I can help you out with my repo.
Maybe you can use socket.io to communicate between the app and webview.

Flutter app restoration; how to save the app state when the activity is killed by the system?

I'm an android developer and I want to switch to flutter. I love the hot reload feature that allows for faster development time. The only thing that prevents me from switching so far is that flutter lacks the option to save the app state when the activity is killed. In native android the option is provided for free (onSaveInstanceState(Bundle savedInstanceState)). So my question is, how can I implement the same feature in Flutter?
Beginning with Flutter 1.22 (which is now in stable), you can make use of RestorationMixin. Here's a full example:
void main() {
runApp(
RootRestorationScope(
restorationId: 'root',
child: MaterialApp(home: HomePage()),
),
);
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with RestorationMixin {
final RestorableInt _counter = RestorableInt(0);
#override
String get restorationId => 'HomePage';
#override
void restoreState(RestorationBucket oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'counter');
}
#override
void dispose() {
_counter.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text('${_counter.value}'),
onPressed: () => setState(() => ++_counter.value),
),
),
);
}
}
How to test:
Go to your device Settings - Developer Options and turn on Don't keep activities.
Now run the app on your Android device, tap the Counters button and hit the home button to force Android to exit your app.
Open the app again and you should see the counter value persist.
You need to use a plugin and method channels in order to know when to persist things, and when to restore things. For that, you can use the following library:
https://github.com/littlerobots/flutter-native-state
It lets you send your state into a Bundle on the Android side, and communicates restored state back to Flutter through method channel.
Thanks to #CopsOnRoad, points below are related to his answer.
We should use RootRestorationScope instead of RestorationScope
like this...
void main() {
runApp(
RootRestorationScope( // <--fix
restorationId: 'root',
child: MaterialApp(home: HomePage()),
),
);
}
also RestorationMixin has generic: RestorationMixin<HomePage> - not necessary, but if you want to declare your HomeRestorationMixin later, and if you want to make correct signature in didUpdateWidget(HomePage widget) method...
also we should manually run dispose() for every property of the State:
so it will be
class _HomePageState extends State<HomePage> with
RestorationMixin<HomePage> { // <-- optional
...
#override
void dispose() {
_counter.dispose(); // <-- recommended
super.dispose();
}
}
One option is WidgetsBindingObserver, only usable with the didChangeAppLifecycleState method, that you can use to handle the AppLifecycleState.

Flutter: Why Future.then() doesn't work on class variable?

Code I show you is the simplified code which I'm troubled in.
My expected result is [1,2,3,4,5,6], but app says [1,2,3].
I know "loadMoreInterger()" should be in "initState()", but for some reason I have to put it in Widget build() {"HERE"}.
I wonder if why doesn't it work, and the solution for correct result.....
I really appreciate for your help :)
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
// ↓↓↓↓↓↓↓↓↓↓↓WHERE I CANNOT UNDERSTAND↓↓↓↓↓↓↓↓↓↓↓
class _MyHomePageState extends State<MyHomePage> {
List<int> intList = [1,2,3];
Future<List<int>> loadMoreInteger() async {
print('Future');
return [4,5,6];
}
#override
Widget build(BuildContext context) {
loadMoreInteger().then((value) {
intList.addAll(value); // why doesn't it work?
});
print("console: $intList");
return Scaffold(
body: Center(
child: Text("display: $intList")
)
);
}
}
//Expected result: [1,2,3,4,5,6]
//Actual result: [1,2,3]
put it in initState override function and it works for yu !!!!
List<int> intList = new List();
Future<List<int>> loadMoreInteger() async {
print('Future');
return [4,5,6];
}
#override
void initState() {
super.initState();
intList = [1,2,3];
loadMoreInteger().then((v){
setState(() {
intList.addAll(v) ;
});
}); }
Here is what your build method does: after entering the method it starts to execute loadMoreInteger() future. Afterwards even if executed future is synchronous it only schedules call of next future that is produced by calling .then. So build method continues to execute with old intList value. And [4,5,6] will be added only after build completes.
In general you can wait for future to complete by calling it with await keyword. But build method is overriden and already has predefined return type that is not future, so you can not call await inside build.
What you can do:
I highly recommend moving any manipulation with data from build method. Its purpose is to produce widgets as fast as possible. It can be called multiple times at some moment unexpected for developer.
One of possible options for you will be moving loadMoreInteger() to initState and calling setState when intList is updated
#override
void initState() {
super.initState();
loadMoreInteger().then((value) {
setState(() {
intList.addAll(value);
});
});
}

Categories

Resources