Is there a way to scan barcodes in Flutter? - 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;
});
}

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?

Programmatically Select Marker to Navigate To Location Google_Maps_Flutter

I am trying to develop a user experience where the user is provided a list of venues to travel to, and upon selecting one of those locations, I want it to
Add a marker to the map
Show an info window
Provide the option to navigate to the
location.
I was able to use the information from this PR (that has no official documentation it seems) to go from this:
To this:
However, showing the marker info still does not make the given marker "active". When I select the marker, the functionality of the plugin actually displays options for navigating to the given location on the bottom right, like this:
What I would like is for the selection of the venue at the bottom of the list to automatically display all three criteria in the third image with one press. That is, once again:
The marker of the location
The info window
The option to navigate to that location.
Right now, a user has to click the entry from the list at the bottom, and then rather un-intuitively click the marker on the map in order for the navigation options to appear.
How would I go about programatically launching the "on-tap" event that happens when the user "taps" the marker when selecting an item from the list?
Here is my current code for changing the viewport, adding a marker, and showing the info window:
Future<void> goSomewhere(id, name, distance, address, lat, lng) async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(target: LatLng(lat, lng), zoom: 14.0)));
setState(() {
_markers.add(Marker(
markerId: MarkerId(id),
position: LatLng(lat, lng),
infoWindow: InfoWindow(
title: name,
snippet: address,
),
onTap: () {
//_pageController.jumpToPage(i);
},
icon: BitmapDescriptor.defaultMarker,
));
print(MarkerId(id));
controller.showMarkerInfoWindow(MarkerId(id));
});
}
Right now, my solution does add the marker and show the info window, but comes up short giving the user options for navigating to the location unless the user clicks the marker. In its current state, I've built something that makes for a very sub-optimal user experience.
I've tried alternative solutions like highlighting the marker, but it does not quite have the functionality I'm looking for. I do not understand what event occurs within the plugin that "selects" the marker on-tap, and all attempts I've found that "programmatically" select the marker do some version of highlighting it. Simply some direction as to where to look to find out what event is being launched when the marker is clicked would be helpful.
I didn't come to a complete answer, but I wanted to share my experience in case anyone else runs into this problem and feels stuck. By going through the Flutter Github / asking around the forums, I was able to get three key take-aways:
As far as I know, there is no way to simulate the "tap" of the marker in Flutter. I tried looking at the Kotlin/Swift APIs for platform channels to write some of this functionality myself, but to be honest it was pretty far out of my wheelhouse and I burned a lot of time on it. This also might result in having to go through the approval process with the nice people at Google for my changes to be accepted, but it's not time that I personally have right now.
I found a package called Maps Launcher that was also mentioned by Craig in the comments section that has the ability to launch the Maps application by two methods - Launch by Query, and Launch by Latitude/Longitude. This functionality is effectively identical to the functionality you get when you tap the marker and then select one of the two maps options on the bottom right.
I discovered that the primary difference between the two
icons that you get when you tap the marker is that one launches
directions by query, and the other by latitude and longitude - just
like the Maps Launcher plugin.
So with these three pieces of information, I redesigned the application to go from this:
To look like this:
Now a user:
Selects a location from the list.
Selecting a new location adds a marker and shows the location's infowindow
Can hit the button on the right to navigate to the given location.
In my opinion, this experience is much more clean - there is no ambiguity between the two maps options you can launch (by query vs by lat/long) when selecting a marker, as a button that launches the maps icon is now on the Listview. This also allows you to navigate to each marker pretty easily just by clicking it in the listview, and then deciding whether or not you want navigation directions by pressing the button on the right.
The code for adding this to your app is pretty simple:
new Flexible(
child: ListView.builder(
itemCount: venues.length,
padding: EdgeInsets.all(4.0),
itemBuilder: (context, index) {
return Card(
color: _selectedIndex != null && _selectedIndex == index
? Colors.greenAccent
: Colors.white,
child: ListTile(
onTap: () {
goSomewhere(
venues[index].venueId,
venues[index].name,
venues[index].distance,
venues[index].formattedAddress,
double.parse(venues[index].latitude),
double.parse(venues[index].longitude)
);
_onSelected(index);
},
title: Text(
venues[index]
.name /*+ ' and ' + venues[index].formattedAddress*/,
style: TextStyle(fontWeight: FontWeight.w500),
),
trailing: Container(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blue,
),
onPressed: () => MapsLauncher.launchQuery(
venues[index].formattedAddress),
child: Icon(Icons.assistant_navigation),
)),
subtitle: Text(venues[index].formattedAddress),
));
},
))
It's a subtle difference, but I've done some testing and my (small) group of users seem to like this much more as it's much more intuitive. Also, the location they get in their Maps application has a recognizable address regardless of which button they press.
While this is not a "full" answer to the original question, I hope that this might help someone on their Flutter journey to stick to the framework - despite some of these early hurdles.
You beat me to it! I was about to answer this question earlier but when I reloaded the page, I saw you already answered it. Though it appears that you already found the solution to your issue, I thought of posting my answer here still. Please see below:
I couldn't find a way to programmatically display the map toolbar(options for navigating to the given location on the bottom right) as you really need to click a marker to show it. But here's a workaround that I thought would fit your use case:
Disable the map toolbar by setting mapToolbarEnabled: false in GoogleMap() widget. (The map toolbar only works for Android anyway)
Create a custom map toolbar that launches Google Maps and Directions URLs from the app. That toolbar only shows when you select a location from the listview or when you click a marker.
I used the url_launcher package to launch the Google Maps and Directions URLs.
Future<void> _launchUrl(bool isDir, double lat, double lon) async {
String url = 'https://www.google.com/maps/search/?api=1&query=$lat,$lon';
if (isDir) {
url = 'https://www.google.com/maps/dir/?api=1&origin=Googleplex&destination=$lat,$lon';
}
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
Then I used the method above on a custom Map toolbar widget
Widget mapToolBar() {
return Row(
children: [
FloatingActionButton(
child: Icon(Icons.map),
backgroundColor: Colors.blue,
onPressed: () {
_launchUrl(false, {your_lat}, {your_lng})
},
),
FloatingActionButton(
child: Icon(Icons.directions),
backgroundColor: Colors.blue,
onPressed: () {
_launchUrl(true, {your_lat}, {your_lng};
},
),
],
);
}
Then put the custom widget in a Stack widget with the Googlemap() widget
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Expanded(
flex: 7,
child: Stack(children: [
GoogleMap(
.
.
.
),
mapToolBar()
]),
),
_pageViewBuilder(context),
]),
);
}

Flutter Localizations with variables

I'm trying to localize my Flutter app by following the documentation.
What I'm trying to achieve is that while building widgets dynamically, I wanted to translate the data which comes from my model. This is what I've tried so far
List.generate(services.length, (index) {
final Service service = services[index];
return Material(
borderRadius: BorderRadius.circular(10.0),
child: Text(
AppLocalizations.of(context).{{service.title}} // Here I wanted to translate the service title
),
);
}
How can I achieve this in Flutter? or any other possible method to translate dynamic contents.
Dynamic translation is not supported by Flutter localization packages. You can try to integrate google translation API in you app. But I strongly convinced that it should be a server-side feature and Flutter client should obtain already translated messages in your models. To achieve this, for example, you can use http-headers with your device locale to tell client's language to server.
Also you need to use arguments for Intl messages according to this guide. Here is an example of message with String argument in AppLocalizations:
String someLocalizedString(String argument) => Intl.message(
'Localized part - $argument',
name: 'someLocalizedString',
locale: localeName,
args: [argument],
);
This string inside .arb file:
"someLocalizedString": "Localized part - {argument}",
"#someLocalizedString": {
"type": "text",
"placeholders": {
"argument": {}
}
}

NFC Detection doesn't work using NfcManager in Flutter/Dart

first time posting here. Am quite new to flutter.
Currently making an app that uses NFC to allow user login, however I'm unable to show the data of the NFC/print it out so I can use it. I want to be able to print out the code on it that is '0248FCF2255E81', but I can only get the metadata and it doesn't seem to be inside that.
RaisedButton(
onPressed: () async {
bool isAvailable =
await NfcManager.instance.isAvailable();
// Start Session
if (isAvailable) {
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
Ndef ndef1 = Ndef.from(tag);
if (ndef1 == null) {
print('Tag is not compatible with NDEF');
return;
} else {
print(await ndef1.read());
}
},
);
} else {
print("NFC Manager is not available");
}
},
child: Text('Open NFC Manager'),
color: Colors.green[300],
),
When I click the button, it opens the NfcManager, and I should be able to get the data from the NFC tag. However, when I hover my phone over the NFC Tag, I get this error
NoSuchMethodError (NoSuchMethodError: The method 'forEach' was called on null. Receiver: null Tried calling: forEach(Closure: (dynamic, dynamic) => Null))
I tried other packages like nfc_in_flutter, but that didn't work for me when trying to fork their project
I also tried flutter_nfc_reader, which did give me the metadata of the NFC tag, but not the data I wanted.
Same thing with nfc_manager, when I hover over the tag after the error pops up, I can see the metadata of the NFC tag as well, but not see the string I wanted to see.
Hope I can find some help here, thanks!
edit: The package I'm using is https://pub.dev/packages/nfc_manager
tried nfc_in_flutter again and it worked, using
NDEFMessage message = await NFC.readNDEF(once: true).first;
print("payload: ${message.tag.id}");

Using Platform-Specific packages in 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),
),
);
}
}