Using Platform-Specific packages in Flutter - flutter

I'm not sure if this is the appropriate stack exchange site but I think it fits more of here than the others that I can think of. Anyways, I work for a mobile app development team and we're looking into using Flutter to develop our future mobile apps as it reduces the amount of work needed when developing for both iOS and Android (we're just a small team).
I read through a bit about Flutter and checked the available packages and the Dart/Flutter Pub and there are some packages that aren't available yet for Flutter that we use for Android and iOS. Take MSAL (Microsoft Authentication Library) for example. After reading through the documentation, I read about Platform Channels and how you can run some KT/Swift etc specific code and return it through something like the MethodChannel in Kotlin but the example from the Flutter docs show an example of only returning specific data types or simple values. What if I wanted to authenticate a user using MSAL? that would involve some UI work that doesn't happen in Flutter specifically since it relies on either the browser or a webview (depending on your MSAL config)
My question here is probably gonna be in 2 main things:
If you have a package that kind of relies on the UI of either iOS or Android for something that isn't gonna take up the entirety of your app's functions, how would it be possible to still use Flutter to develop the rest of your apps while still making use of the platform-specific packages?
Is it possible to have like more than 1 Activity or ViewController that does this and then go to the Flutter part afterwards? Cuz I think thats one possible solution to the previous question.
Note:
I'm aware that there are packages for authentication in the Dart Pub but I'm just using MSAL as an example, we also use other packages that kind of rely on displaying custom views to authenticate users.

I got this to work using platform channels. To answer my specific questions:
This can still be handled by platform channels. As an example, I am using MSAL with a webview and it still returns to the original FlutterActivity after authenticating my user.
I am not 100% sure about this because I didn't create an Activity myself but the package was able to open its own webview so it should work
MainActivity
private val LOGIN_CHANNEL = "flutter.android/msal"
private val scopes = arrayOf("https://graph.microsoft.com/User.Read", "https://graph.microsoft.com/User.ReadBasic.All")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MyLibrary.setupClientApp(applicationContext, R.raw.auth_config, scopes)
MethodChannel(flutterView, LOGIN_CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "login") {
login{
result.success(it)
}
}
}
}
private fun login(callback: (String?) -> Unit) {
MyLibrary.instance!!.acquireToken(
this,
scopes,
MyLibrary.getAuthInteractiveCallback {
callback(MyLibrary.getUser()?.displayName)
}
)
}
MyHomePage (State)
class _MyHomePageState extends State<MyHomePage> {
String _responseFromNativeCode = "";
static const platform = const MethodChannel('flutter.android/msal');
Future _login() async {
String response = "";
try {
response = await platform.invokeMethod('login');
} on PlatformException catch (e) {
response = "Failed to Invoke: '${e.message}'.";
}
setState(() {
_responseFromNativeCode = response;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'Hi $_responseFromNativeCode',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _login,
tooltip: 'Login',
child: Icon(Icons.lock),
),
);
}
}

Related

Flutter in_app_update gives updateAvailability 1 : unable to make it work

I'm sorry if I am making a mistake here, but I think I've tried everything in my power to make it work and I couldn't, also I think there might be something going on here since new_version package isn't working right now (there is a thread on github currently running to solve it):
I am using flutter's in_app_update package https://pub.dev/packages/in_app_update to provide to the user, as you guessed it, app updates.
Code :
bool _once = true;
Future<void> checkForUpdate() async {
InAppUpdate.checkForUpdate().then((info) {
setState(() {
_updateInfo = info;
});
}).catchError((e) {
Get.defaultDialog(
title: "Error",
titleStyle: TextStyle(color: Colors.white),
backgroundColor: Color(0xff7C695F),
content: MyErrorWidget(e.toString(), false));
});
}
...
#override
Widget build(BuildContext context) {
if (_once) {
WidgetsBinding.instance?.addPostFrameCallback((_) => checkForUpdate());
_once = false;
}
...
Center(
child: Text('Update info: $_updateInfo'),
),
ElevatedButton(
child: Text('Update available : Perform Update'),
onPressed: _updateInfo?.updateAvailability ==
UpdateAvailability.updateAvailable
? () {
InAppUpdate.performImmediateUpdate().catchError((e) {
showSnack(e.toString());
});
}
: null),
...
I know that this package is made for release mode, and I am receiving this screen when running in debug mode, which is normal (maybe?)Screenshot of app.
I am aware that you have to make two bundles with different versions codes but same signatures, upload them to Internal Testing inside the play store( so that google play store signs the versions). Install the lowest number version (which for me was versionCode : 11) and then push a new appbundle with new version number ( for me : 12).
As I launched the play store again I indeed noticed that a new update was available :
Play store
But when launching the app (version code 11), The message that is always displayed is the one from 1, and there is no way to debug anything since it is only release mode, also I can't use the package new_version since changes were made to the play store pages which makes it unavailable atm.
Do you guys have any advice?

flutter ,My app dies whenever i enter the videoPlayerScreen

it's all good on android but not good on iOS.
whenever i try to enter the videoPlayerScreen
error occurs
I tryed another project with same class code ,
it was fine.
but in this project doens't work.
Here's my code
...
class _VideoTestState extends State<VideoTest> {
final FijkPlayer player = FijkPlayer();
#override
void initState() {
super.initState();
player.setDataSource( // ✅ App dies in here
'http://baishan.oversketch.com/2019/11/05/d07f2f1440e51b9680f4bcfe63b0ab67.MP4',
autoPlay: true);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.green,
child: Center(
child: FijkView(
player: player,
),
),
),
);
}
}
in Android studio when I enter it , it dies immediately, Log( Lost connection to device. )
but in Xcode show log like this.
I assume its package problem.
not sure about it but only difference between another project and this one is
another project has few package ( actually minimum packages to play video )
this one has alot include firebase things.
I tried ,
flutter clean ,
rm Podfile.lock
pod install.
is there any clue to solve this problem??

How to send app in background when back button is pressed

I'm trying to avoid the default behaviour of Flutter, instead of closing app when the back button (of the smartophone of course) is pressed I will send the app in background, so when I try to reopen the app it returns on the last screen.
I also tried a solution that I have found here, but didn't work.
This is the code:
return WillPopScope(
onWillPop: () async =>
await SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
It also closes the app and don't send it in background.
The screen that contains the code showed above is the last route.
You can use MethodChannel for this, invoke a method from Flutter that will trigger a method in Java.
// in flutter use something like this
methodChannel.invokeMethod("homeButton");
And in Java, you can create a method like:
public void homeButton() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
More info on how to write platform specific code.
Flutter plugin for sending mobile applications to the background. Supports iOS and Android.
move_to_background: ^1.0.2
WillPopScope(
child: MaterialApp(...),
onWillPop: () async {
MoveToBackground.moveTaskToBack();
return false;
},
);

Flutter - Keep page static throughout lifecycle of app?

I have created an AppDrawer widget to wrap my primary drawer navigation and reference it in a single place, like so:
class AppDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: new ListView(
children: <Widget>[
new ListTile(
title: new Text("Page1"),
trailing: new Icon(Icons.arrow_right),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => Page1.singleInstance));
}
),
new ListTile(
title: new Text("Page2"),
trailing: new Icon(Icons.arrow_right),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new Page2("Page 2")));
}
),
]
),
);
}
}
I have also created a custom AppScaffold widget, which simply returns a consistent AppBar, my custom AppDrawer, and body:
class AppScaffold extends StatelessWidget {
final Widget body;
final String pageTitle;
AppScaffold({this.body, this.pageTitle});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(title: new Text(pageTitle), backgroundColor: jet),
drawer: AppDrawer(),
body: body
);
}
}
I have created two pages: Page1, and Page2. They are simple right now, and look something like this:
class Page1 extends StatelessWidget {
final String pageText;
Page1(this.pageText);
static Page1 get singleInstance => Page1("Page1");
Widget build(BuildContext context) {
return AppScaffold(
pageTitle: this.pageText,
body: SafeArea(
child: Stack(
children: <Widget>[
Center(child: SomeCustomWidget())
],
)
),
);
}
}
class Page2 extends StatelessWidget {
final String pageText;
Page2(this.pageText);
#override
Widget build(BuildContext context) {
return AppScaffold(
pageTitle: this.pageText,
body: SafeArea(
child: Stack(
children: <Widget>[
Center(child: SomeOtherCustomWidget())
],
)
),
);
}
}
When I run my app, I can see the navbar and drawer correctly. I can click on the links in the drawer to navigate between my pages. However, each time I navigate to a page, all of the widgets on that page get reset to their initial state. I want to ensure that the widgets do not get reset. Another way to think of this is: I only want one instance of each page throughout the lifecycle of the app, instead of creating them new whenever a user navigates to them.
I tried creating a static instance of Page1 that the Drawer uses when the onTap event is fired, but this does not work. Am I thinking about this incorrectly? Do I need to convert to a Stateful widget?
Oh, you're in for a treat... This will be kinda long (sorry) but please read all of it before making decisions and taking action - I promise I am saving you time.
There are many different solutions to this problem, but in general what you're asking about is state management (which is really software engineering, more info here - Understanding state management, and why you never will).
I'll try my best to explain what is happening in your specific case...
Problem:
Think of Navigator as a List of application states, which you can manipulate via its various methods (i.e. pop(), push(), etc.), with this in mind it is clear what is happening - on a button press you're actually removing the current state (page) and right after that you're pushing a new instance of your state (page).
Solution(s):
As I said, there are many solutions to this problem, for example, you may be tempted to store the state (the changes you made to a particular "page") somewhere in a var and inject that var when navigating between "pages", when creating a new instance of that page, but you'll soon run into other problems. This is why I don't think anyone can provide a simple solution to this problem...
First, may I suggest you some useful reads on the matter:
Flutter official docs on state management - When you get to the "Options" section of this, the fun part begins and can quickly get overwhelming, but fear not :P
Be sure to read the medium article mentioned in the start of my answer too, I found it really helpful.
These reads will be more than enough to help you make a decision, plus there are a ton of articles on Medium and YouTube videos touching on the matter of state management with Flutter (even some from the authors of the framework) - just search for "State management with Flutter".
Now my own personal opinion:
If it's a really simple use case and you don't plan to grow (which is almost never the case, trust me), you can just use StatefulWidgets in combination with setState() and maybe InheritedWidget (for dependency injection down the tree, or like React guys call it "lifting state up"). Or instead of the above, maybe have a look at scoped_model, which kinda abstracts all of this for you (tho, I haven't played with it).
What I use right now for a real world project is bloc and flutter_bloc (BLoC = Business Logic Component), I will not get into the details of it, but basically it takes the idea of scoped_model one step further, without over-complicating abstractions. bloc is responsible for abstracting away the "business logic" of your application and flutter_bloc to "inject" the state in your UI and react to state changes (official Flutter position on the matter is that UI = f(State)).
A BLoC has an input and an output, it takes in events as an input (can be user input, or other, any type of event really) and produces a state. In summary that's it about bloc.
A great way to get started is BLoC's official documentation. I highly recommend it. Just go through everything.
(p.s. This may be my personal opinion, but in the end state management in Flutter is all based on some form of using InheritedWidget and setState() in response to user input or other external factors that should change the application state, so I think the BLoC pattern is really on point with abstracting those :P)

Is there a way to scan barcodes in Flutter?

Basically, I'm making an app that scans a QR code to connect to a server. Then, the app will scan the barcode of products and take pictures of the item and send them to the server. My question is the following :
Is there a Flutter plugin to scan QR codes and barcodes that doesn't enter in conflict with image_picker?
Here's what I found so far.
barcode_scan. Works well until you add a dependency on camera or image_picker. Issue.
BarcodeScannerPlugin
. An issue is open, which have the same problem as the previous plugin.
flutter_qrcode_reader, deprecated. Apparently, it doesn't build.
flutterZebraEmdk is an empty project without a README.md.
flutter_qr_mobile_vision, doesn't support barcodes. Issue.
I appreciate any help you can provide. Thanks!
Update
The issue with barcode_scan was resolved. I ended up using this one since it's faster than the accepted answer and its issues got resolved rather quickly. Be aware that its behaviour on iOS is modified by Apple, so you might get different results with checksum numbers or something.
I've previously had a similar problem, and after searching as you did I didn't find a whole lot. I decided that the best approach would be to write a plugin myself... so shameless plug for my plugin here =D, not that I benefit from anyone else using it at all.
You can see it here. However, I haven't had time to document it, test it all that extensively, or publish it properly on Pub. So your mileage may vary. It should however work on android 4.4+ (and maybe below), and iOS devices that flutter supports. I also haven't tested it in conjunction with the Camera plugin but I don't see why it would have a problem with it.
It takes a different approach than most of the other qr code plugins; instead of making an android or iOS window, doing the scan, then returning to flutter, it uses flutter's texture rendering capabilities to have the camera render directly into flutter.
A few more things to consider are that it uses the Google Mobile Vision SDK with the applicable licensing and capabilities that comes along with that (and requires a recent version of Play Services on Android); and that it currently only supports the most basic extraction of information from barcode scans - I only needed the raw text out so that's all I did.
To use it, add this to your pubspec.yaml:
dependencies:
qr_mobile_vision: '^0.0.7'
And implement as follows:
import 'package:qr_mobile_vision/QrCamera.dart';
...
new Container(
constraints: new BoxConstraints.loose(
new Size(cameraSize, cameraSize)),
child: new QrCamera(
qrCodeCallback: (code) {
print(code);
}
),
)
I do plan on finishing documentation/testing/etc eventually, but you're welcome to try it out in the meantime. If you decide to use it and need a feature it doesn't support I may be able to help implement it... but PRs are welcome and encouraged!
UPDATE: this does include Barcode support now. You can pass in which types of QR code / Barcode you want to support when you instantiate QrCamera. It defaults to all, which takes more processing so if you're after a certain type it's recommended that you pass it in.
I'm working on something currently as a companion to my QR generation plugin (https://github.com/lukef/qr.flutter) but I don't have a specific timeline, unfortunately.
My plan is to use the Texture object and hookup a camera (or fork / use the camera plugin) and then use the Google Vision API (https://developers.google.com/vision/android/barcodes-overview).
It should be decently trivial, I just need to find the time. Either way, that was the plan if you want to do it :)
You can use an open source SDK (e.g., ZXing) or a commercial SDK (e.g., Dynamsoft Barcode Reader SDK) in your Flutter project. Implementing the barcode scanning function is easy.
I have written an article - Flutter Programming with Android AAR File, sharing how to scan QR code in a flutter project. The source code is also available on GitHub.
Java code
private String onGetBarcode(String json) {
String filename;
try {
JSONObject message = new JSONObject(json);
filename = message.getString("filename");
} catch (JSONException e) {
Log.e(TAG, "JSON exception", e);
return null;
}
String locationProvider;
String barcodeResult = "No barcode detected";
File file = new File(filename);
if (!file.exists()) {
barcodeResult = "No file exists: " + file.toString();
Toast.makeText(BarcodeReaderActivity.this, barcodeResult, Toast.LENGTH_LONG).show();
return null;
}
else {
Bitmap bitmap = BitmapFactory.decodeFile(file.toString());
BarcodeReader reader = new BarcodeReader("license");
ReadResult result = reader.readSingle(bitmap, Barcode.QR_CODE);
Barcode[] all = result.barcodes;
if (all != null && all.length == 1) {
barcodeResult = all[0].displayValue;
}
else {
barcodeResult = "no barcode found: " + file.toString();
}
bitmap.recycle();
}
JSONObject reply = new JSONObject();
try {
if (barcodeResult != null) {
reply.put("result", barcodeResult);
} else {
reply.put("result", "No barcode detected");
}
} catch (JSONException e) {
Log.e(TAG, "JSON exception", e);
return null;
}
return reply.toString();
}
Dart code
#override
Widget build(BuildContext context) {
if (_isExisted) {
return new Material(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text('Barcode Reader'),
new Input(
labelText: 'Please input the image path',
value: new InputValue(text: _filename),
onChanged: onTextChanged,
autofocus: true,
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
child: new Text('Read'),
onPressed: _getBarcode
),
new RaisedButton(
child: new Text('Reset'),
onPressed: _resetResult
),
]
),
new Image.file(new File(_filename)),
new Text('$_result'),
]
)
)
);
}
else {
return new Material(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text('Barcode Reader'),
new Input(
labelText: 'Please input the image path',
onChanged: onTextChanged,
autofocus: true,
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
child: new Text('Read'),
onPressed: _getBarcode
),
new RaisedButton(
child: new Text('Reset'),
onPressed: _resetResult
),
]
),
new Text('$_result'),
]
)
)
);
}
}
Future<Null> _readBarcode() async {
final Map<String, String> message = <String, String>{'filename':_filename};
final Map<String, dynamic> reply = await HostMessages.sendJSON('getBarcode', message);
// If the widget was removed from the tree while the message was in flight,
// we want to discard the reply rather than calling setState to update our
// non-existent appearance.
if (!mounted)
return;
setState(() {
_result = reply['result'].toString();
});
}
Screenshot
So take a little bit of time and do it yourself :)
I used qr_scan. The issue was I created my flutter with objective c. I had to delete iOS and change it.
To do that delete the ios folder from the project and create it with swift.
To create use is command flutter create -i swift . dont forget the . in the end cause it took me a couple of hours to figure that out
According with my researchs there are good options for that:
flutter_barcode_scanner (reader)
barcode_scan (reader)
qr_flutter (thats is for create QR codes)
1st: barcode_scan will works as a wrapper for two commonly used iOS and Android libraries.
(iOS: https://github.com/mikebuss/MTBBarcodeScanner, Android: https://github.com/dm77/barcodescanner)
They are in version 3 already and seems like the all the major bugs were resolved.
dependencies:
barcode_scan: ^3.0.1
Here the feature list:
[x] Scan 2D barcodes
[x] Scan QR codes
[x] Control the flash while scanning
[x] Permission handling
The main warn is about https://github.com/dm77/barcodescanner that's no longer maintained.
The 2nd option is flutter_barcode_scanner thats also works for both android and ios.
https://pub.dev/packages/flutter_barcode_scanner
For android is just put the pubdev dependency and play, for iOS the only things you need to do is: 1: set minimum deployment target to 11, 2: set Swift version to 5 and 3: ask the permission to use the camera. May the flutter-bardode-scanner is best option since it will depends on just a pubdev dependency without the need of 3rd party project (no longer maintained).
When it's needed to create QR code there is: QR.Flutter also works for ios and android.
Features:
[x] Built on QR - Dart
[x] Automatic QR code version/type detection or manual entry
[x] Supports QR code versions 1 - 40
[x] Error correction / redundancy
[x] Configurable output size, padding, background and foreground colors
[x] Supports image overlays
[x] Export to image data to save to file or use in memory
[x] No internet connection required
Both of them have the best reputation on pub.dev for this propose. So may you need to give a try and check which of them will fill your project needs.
Updating the answer: To read barcode from image gallery there is an alternative package called qr_code_tools. The image could be get from ImagePicker and then decoded to data through QrCodeToolsPlugin.decodeFrom
Getting the image from galley:
ImagePicker.pickImage(source: ImageSource.gallery));
Decoding the image to data:
import 'package:qr_code_tools/qr_code_tools.dart';
String _data;
Future decode(String file) async {
String data = await QrCodeToolsPlugin.decodeFrom(file);
setState(() {
_data = data;
});
}