How to debounce action in Flutter

Issue #293

Answer https://stackoverflow.com/a/55119208/1418457


This is useful to throttle TextField change event. You can make Debouncer class using Timer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:flutter/foundation.dart';
import 'dart:async';

class Debouncer {
final int milliseconds;
VoidCallback action;
Timer _timer;

Debouncer({ this.milliseconds });

run(VoidCallback action) {
if (_timer != null) {
_timer.cancel();
}

_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}

Declare and trigger

1
2
3
4
5
final _debouncer = Debouncer(milliseconds: 500);

onTextChange(String text) {
_debouncer.run(() => print(text));
}

How to resolve deep json object in Dart

Issue #198

If we are not on the edge with GRPC and Protocol Buffer, then most likely we are going to deal with Restful and JSON. In one of my Flutter apps I needed to consume JSON

JSON and serialization

The guide at https://flutter.dev/docs/development/data-and-backend/json is definitely the way to go.

Currently there are 2 ways. One is to manually use dart:convert package

1
Map<String, dynamic> user = jsonDecode(jsonString);

The other way is to use json_serializable to generate parsing code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import 'package:json_annotation/json_annotation.dart';

/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';

/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
@JsonSerializable()

class User {
User(this.name, this.email);

String name;
String email;

/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case User.
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$UserToJson(this);
}

json_resolve

The problem with manual approach is that it involves lot of boilerplate code, especially when accessing property inside deeply nested json. The problem with code generation approach is that it does not always fit our need and may lack of customization.

Therefore I created json_resolve which allows us to access json using keypath, with type checking and safety in mind. The code is small, simple to reason and tested.

1
2
3
4
5
6
7
8
9
10
11
final String byProperty = resolve(json: json, path: "movie", defaultValue: "error");
expect(byProperty, "isFun");

final int byInt = resolve(json: json, path: "earth", defaultValue: 0);
expect(byInt, 199999);

final String byIndex = resolve(json: json, path: "dc.2.name", defaultValue: "error");
expect(byIndex, "Wonder Woman");

final String byIndexThenProperty = resolve(json: json, path: "marvel.0.appear.1.title", defaultValue: "error");
expect(byIndexThenProperty, "The Dark World");

How to fix ApiException 10 in Flutter for Android

Issue #188

Get error com.google.android.gms.common.api.ApiException: 10 with google_sign_in package.

Read https://developers.google.com/android/guides/client-auth

Certain Google Play services (such as Google Sign-in and App Invites) require you to provide the SHA-1 of your signing certificate so we can create an OAuth2 client and API key for your app

console.developers.google.com/apis/credentials

Credentials -> OAuth client id
If we specify SHA1 in firebase, then console.developers.google.com will generate an Android oauth for us

1
keytool -list -v -keystore {keystore_name} -alias {alias_name}

Use correct keystore for debug and release

1
2
3
4
5
6
7
8
9
10
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}

How to get Google account photo in Flutter

Issue #187

If you use Flutter, then you can access it via people.googleapis.com endpoint, code uses google_sign_in library

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:google_sign_in/google_sign_in.dart';

Future<String> getPhotoUrl(GoogleSignInAccount account, String userId) async {
// final authentication = await account.authentication;
final url = 'https://people.googleapis.com/v1/people/${userId}?personFields=photos';
final response = await http.get(
url,
headers: await account.authHeaders
);

final data = json.decode(response.body);
return data['photos'].first['url'];
}

You will get something like

1
2
3
4
5
6
{
resourceName: people/998812322529259873423,
etag: %EgQBAzcabcQBAgUH,
photos: [{metadata: {primary: true, source: {type: PROFILE, id: 107721622529987673423}},
url: https://lh3.googleusercontent.com/a-/abcdefmB2p1VWxLsNT9WSV0yqwuwo6o2Ba21sh_ra7CnrZ=s100}]
}

where url is an accessible image url.