update for null safety (#145)
update for null safety
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 4d33943..446e513 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -11,7 +11,7 @@
runs-on: ubuntu-latest
container:
- image: google/dart:beta
+ image: google/dart:dev
steps:
- uses: actions/checkout@v2
diff --git a/.gitignore b/.gitignore
index 714afaf..b77fd3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
# https://dart.dev/guides/libraries/private-files
.packages
.dart_tool/
+.idea/
pubspec.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7a3c57..fdc54f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 4.0.0-nullsafety
+- Updated to support 2.12.0 and null safety.
+
## 3.4.2
- A number of cleanups to improve the package health score.
diff --git a/example/example.dart b/example/example.dart
index bb7b345..46cc29b 100644
--- a/example/example.dart
+++ b/example/example.dart
@@ -9,26 +9,26 @@
import 'package:usage/usage_html.dart';
-Analytics _analytics;
-String _lastUa;
+Analytics? _analytics;
+String? _lastUa;
int _count = 0;
void main() {
- querySelector('#foo').onClick.listen((_) => _handleFoo());
- querySelector('#bar').onClick.listen((_) => _handleBar());
- querySelector('#page').onClick.listen((_) => _changePage());
+ querySelector('#foo')!.onClick.listen((_) => _handleFoo());
+ querySelector('#bar')!.onClick.listen((_) => _handleBar());
+ querySelector('#page')!.onClick.listen((_) => _changePage());
}
-String _ua() => (querySelector('#ua') as InputElement).value.trim();
+String _ua() => (querySelector('#ua') as InputElement).value!.trim();
Analytics getAnalytics() {
if (_analytics == null || _lastUa != _ua()) {
_lastUa = _ua();
- _analytics = AnalyticsHtml(_lastUa, 'Test app', '1.0');
- _analytics.sendScreenView(window.location.pathname);
+ _analytics = AnalyticsHtml(_lastUa!, 'Test app', '1.0');
+ _analytics!.sendScreenView(window.location.pathname!);
}
- return _analytics;
+ return _analytics!;
}
void _handleFoo() {
@@ -44,5 +44,5 @@
void _changePage() {
var analytics = getAnalytics();
window.history.pushState(null, 'new page', '${++_count}.html');
- analytics.sendScreenView(window.location.pathname);
+ analytics.sendScreenView(window.location.pathname!);
}
diff --git a/lib/src/usage_impl.dart b/lib/src/usage_impl.dart
index fc87e95..07837d4 100644
--- a/lib/src/usage_impl.dart
+++ b/lib/src/usage_impl.dart
@@ -25,10 +25,9 @@
class ThrottlingBucket {
final int startingCount;
int drops;
- int _lastReplenish;
+ late int _lastReplenish;
- ThrottlingBucket(this.startingCount) {
- drops = startingCount;
+ ThrottlingBucket(this.startingCount) : drops = startingCount {
_lastReplenish = DateTime.now().millisecondsSinceEpoch;
}
@@ -61,9 +60,9 @@
@override
final String trackingId;
@override
- final String applicationName;
+ final String? applicationName;
@override
- final String applicationVersion;
+ final String? applicationVersion;
final PersistentProperties properties;
final PostHandler postHandler;
@@ -76,22 +75,20 @@
@override
AnalyticsOpt analyticsOpt = AnalyticsOpt.optOut;
- String _url;
+ late String _url;
final StreamController<Map<String, dynamic>> _sendController =
StreamController.broadcast(sync: true);
AnalyticsImpl(this.trackingId, this.properties, this.postHandler,
- {this.applicationName, this.applicationVersion, String analyticsUrl}) {
- assert(trackingId != null);
-
+ {this.applicationName, this.applicationVersion, String? analyticsUrl}) {
if (applicationName != null) setSessionValue('an', applicationName);
if (applicationVersion != null) setSessionValue('av', applicationVersion);
_url = analyticsUrl ?? _defaultAnalyticsUrl;
}
- bool _firstRun;
+ bool? _firstRun;
@override
bool get firstRun {
@@ -103,7 +100,7 @@
}
}
- return _firstRun;
+ return _firstRun!;
}
@override
@@ -120,7 +117,7 @@
}
@override
- Future sendScreenView(String viewName, {Map<String, String> parameters}) {
+ Future sendScreenView(String viewName, {Map<String, String>? parameters}) {
var args = <String, dynamic>{'cd': viewName};
if (parameters != null) {
args.addAll(parameters);
@@ -130,7 +127,7 @@
@override
Future sendEvent(String category, String action,
- {String label, int value, Map<String, String> parameters}) {
+ {String? label, int? value, Map<String, String>? parameters}) {
var args = <String, dynamic>{'ec': category, 'ea': action};
if (label != null) args['el'] = label;
if (value != null) args['ev'] = value;
@@ -148,7 +145,7 @@
@override
Future sendTiming(String variableName, int time,
- {String category, String label}) {
+ {String? category, String? label}) {
var args = <String, dynamic>{'utv': variableName, 'utt': time};
if (label != null) args['utl'] = label;
if (category != null) args['utc'] = category;
@@ -157,12 +154,12 @@
@override
AnalyticsTimer startTimer(String variableName,
- {String category, String label}) {
+ {String? category, String? label}) {
return AnalyticsTimer(this, variableName, category: category, label: label);
}
@override
- Future sendException(String description, {bool fatal}) {
+ Future sendException(String description, {bool? fatal}) {
// We trim exceptions to a max length; google analytics will apply it's own
// truncation, likely around 150 chars or so.
const maxExceptionLength = 1000;
@@ -201,7 +198,7 @@
Stream<Map<String, dynamic>> get onSend => _sendController.stream;
@override
- Future waitForLastPing({Duration timeout}) {
+ Future waitForLastPing({Duration? timeout}) {
Future f = Future.wait(_futures).catchError((e) => null);
if (timeout != null) {
@@ -268,6 +265,7 @@
PersistentProperties(this.name);
dynamic operator [](String key);
+
void operator []=(String key, dynamic value);
/// Re-read settings from the backing store. This may be a no-op on some
diff --git a/lib/src/usage_impl_html.dart b/lib/src/usage_impl_html.dart
index f2de075..2d8092d 100644
--- a/lib/src/usage_impl_html.dart
+++ b/lib/src/usage_impl_html.dart
@@ -15,38 +15,39 @@
class AnalyticsHtml extends AnalyticsImpl {
AnalyticsHtml(
String trackingId, String applicationName, String applicationVersion,
- {String analyticsUrl})
+ {String? analyticsUrl})
: super(trackingId, HtmlPersistentProperties(applicationName),
HtmlPostHandler(),
applicationName: applicationName,
applicationVersion: applicationVersion,
analyticsUrl: analyticsUrl) {
- var screenWidth = window.screen.width;
- var screenHeight = window.screen.height;
+ var screenWidth = window.screen!.width;
+ var screenHeight = window.screen!.height;
setSessionValue('sr', '${screenWidth}x$screenHeight');
- setSessionValue('sd', '${window.screen.pixelDepth}-bits');
+ setSessionValue('sd', '${window.screen!.pixelDepth}-bits');
setSessionValue('ul', window.navigator.language);
}
}
typedef HttpRequestor = Future<HttpRequest> Function(String url,
- {String method, dynamic sendData});
+ {String? method, dynamic sendData});
class HtmlPostHandler extends PostHandler {
- final HttpRequestor mockRequestor;
+ final HttpRequestor? mockRequestor;
HtmlPostHandler({this.mockRequestor});
@override
Future sendPost(String url, Map<String, dynamic> parameters) {
- var viewportWidth = document.documentElement.clientWidth;
- var viewportHeight = document.documentElement.clientHeight;
+ var viewportWidth = document.documentElement!.clientWidth;
+ var viewportHeight = document.documentElement!.clientHeight;
parameters['vp'] = '${viewportWidth}x$viewportHeight';
var data = postEncode(parameters);
- var requestor = mockRequestor ?? HttpRequest.request;
+ Future<HttpRequest> Function(String, {String method, dynamic sendData})
+ requestor = mockRequestor ?? HttpRequest.request;
return requestor(url, method: 'POST', sendData: data).catchError((e) {
// Catch errors that can happen during a request, but that we can't do
// anything about, e.g. a missing internet connection.
@@ -58,7 +59,7 @@
}
class HtmlPersistentProperties extends PersistentProperties {
- Map _map;
+ late Map _map;
HtmlPersistentProperties(String name) : super(name) {
var str = window.localStorage[name];
diff --git a/lib/src/usage_impl_io.dart b/lib/src/usage_impl_io.dart
index e942c89..eddc1ef 100644
--- a/lib/src/usage_impl_io.dart
+++ b/lib/src/usage_impl_io.dart
@@ -24,7 +24,7 @@
class AnalyticsIO extends AnalyticsImpl {
AnalyticsIO(
String trackingId, String applicationName, String applicationVersion,
- {String analyticsUrl, Directory documentDirectory})
+ {String? analyticsUrl, Directory? documentDirectory})
: super(
trackingId,
IOPersistentProperties(applicationName,
@@ -75,9 +75,9 @@
class IOPostHandler extends PostHandler {
final String _userAgent;
- final HttpClient mockClient;
+ final HttpClient? mockClient;
- HttpClient _client;
+ HttpClient? _client;
IOPostHandler({this.mockClient}) : _userAgent = _createUserAgent();
@@ -87,11 +87,11 @@
if (_client == null) {
_client = mockClient ?? HttpClient();
- _client.userAgent = _userAgent;
+ _client!.userAgent = _userAgent;
}
try {
- var req = await _client.postUrl(Uri.parse(url));
+ var req = await _client!.postUrl(Uri.parse(url));
req.write(data);
var response = await req.close();
await response.drain();
@@ -108,10 +108,10 @@
JsonEncoder _jsonEncoder = JsonEncoder.withIndent(' ');
class IOPersistentProperties extends PersistentProperties {
- File _file;
- Map _map;
+ late File _file;
+ late Map _map;
- IOPersistentProperties(String name, {String documentDirPath}) : super(name) {
+ IOPersistentProperties(String name, {String? documentDirPath}) : super(name) {
var fileName = '.${name.replaceAll(' ', '_')}';
documentDirPath ??= userHomeDir();
_file = File(path.join(documentDirPath, fileName));
@@ -162,18 +162,15 @@
/// Return the string for the platform's locale; return's `null` if the locale
/// can't be determined.
-String getPlatformLocale() {
+String? getPlatformLocale() {
var locale = Platform.localeName;
- if (locale == null) return null;
- if (locale != null) {
- // Convert `en_US.UTF-8` to `en_US`.
- var index = locale.indexOf('.');
- if (index != -1) locale = locale.substring(0, index);
+ // Convert `en_US.UTF-8` to `en_US`.
+ var index = locale.indexOf('.');
+ if (index != -1) locale = locale.substring(0, index);
- // Convert `en_US` to `en-us`.
- locale = locale.replaceAll('_', '-').toLowerCase();
- }
+ // Convert `en_US` to `en-us`.
+ locale = locale.replaceAll('_', '-').toLowerCase();
return locale;
}
diff --git a/lib/usage.dart b/lib/usage.dart
index 82a5b8f..4136d51 100644
--- a/lib/usage.dart
+++ b/lib/usage.dart
@@ -44,10 +44,10 @@
String get trackingId;
/// The application name.
- String get applicationName;
+ String? get applicationName;
/// The application version.
- String get applicationVersion;
+ String? get applicationVersion;
/// Is this the first time the tool has run?
bool get firstRun;
@@ -72,7 +72,7 @@
///
/// [parameters] can be any analytics key/value pair. Useful
/// for custom dimensions, etc.
- Future sendScreenView(String viewName, {Map<String, String> parameters});
+ Future sendScreenView(String viewName, {Map<String, String>? parameters});
/// Sends an Event hit to Google Analytics. [label] specifies the event label.
/// [value] specifies the event value. Values must be non-negative.
@@ -80,7 +80,7 @@
/// [parameters] can be any analytics key/value pair. Useful
/// for custom dimensions, etc.
Future sendEvent(String category, String action,
- {String label, int value, Map<String, String> parameters});
+ {String? label, int? value, Map<String, String>? parameters});
/// Sends a Social hit to Google Analytics.
///
@@ -96,17 +96,17 @@
/// milliseconds). [category] specifies the category of the timing. [label]
/// specifies the label of the timing.
Future sendTiming(String variableName, int time,
- {String category, String label});
+ {String? category, String? label});
/// Start a timer. The time won't be calculated, and the analytics information
/// sent, until the [AnalyticsTimer.finish] method is called.
AnalyticsTimer startTimer(String variableName,
- {String category, String label});
+ {String? category, String? label});
/// In order to avoid sending any personally identifying information, the
/// [description] field must not contain the exception message. In addition,
/// only the first 100 chars of the description will be sent.
- Future sendException(String description, {bool fatal});
+ Future sendException(String description, {bool? fatal});
/// Gets a session variable value.
dynamic getSessionValue(String param);
@@ -136,7 +136,7 @@
/// allows CLI apps to delay for a short time waiting for GA requests to
/// complete, and then do something like call `dart:io`'s `exit()` explicitly
/// themselves (or the [close] method below).
- Future waitForLastPing({Duration timeout});
+ Future waitForLastPing({Duration? timeout});
/// Free any used resources.
///
@@ -157,11 +157,11 @@
class AnalyticsTimer {
final Analytics analytics;
final String variableName;
- final String category;
- final String label;
+ final String? category;
+ final String? label;
- int _startMillis;
- int _endMillis;
+ late int _startMillis;
+ int? _endMillis;
AnalyticsTimer(this.analytics, this.variableName,
{this.category, this.label}) {
@@ -172,7 +172,7 @@
if (_endMillis == null) {
return DateTime.now().millisecondsSinceEpoch - _startMillis;
} else {
- return _endMillis - _startMillis;
+ return _endMillis! - _startMillis;
}
}
@@ -220,7 +220,7 @@
String get clientId => '00000000-0000-4000-0000-000000000000';
@override
- Future sendScreenView(String viewName, {Map<String, String> parameters}) {
+ Future sendScreenView(String viewName, {Map<String, String>? parameters}) {
parameters ??= <String, String>{};
parameters['viewName'] = viewName;
return _log('screenView', parameters);
@@ -228,7 +228,7 @@
@override
Future sendEvent(String category, String action,
- {String label, int value, Map<String, String> parameters}) {
+ {String? label, int? value, Map<String, String>? parameters}) {
parameters ??= <String, String>{};
return _log(
'event',
@@ -242,7 +242,7 @@
@override
Future sendTiming(String variableName, int time,
- {String category, String label}) {
+ {String? category, String? label}) {
return _log('timing', {
'variableName': variableName,
'time': time,
@@ -253,12 +253,12 @@
@override
AnalyticsTimer startTimer(String variableName,
- {String category, String label}) {
+ {String? category, String? label}) {
return AnalyticsTimer(this, variableName, category: category, label: label);
}
@override
- Future sendException(String description, {bool fatal}) =>
+ Future sendException(String description, {bool? fatal}) =>
_log('exception', {'description': description, 'fatal': fatal});
@override
@@ -271,7 +271,7 @@
Stream<Map<String, dynamic>> get onSend => _sendController.stream;
@override
- Future waitForLastPing({Duration timeout}) => Future.value();
+ Future waitForLastPing({Duration? timeout}) => Future.value();
@override
void close() {}
@@ -300,7 +300,7 @@
iter = iter.toList().reversed;
for (var match in iter) {
- var replacement = match.group(1);
+ var replacement = match.group(1)!;
str =
str.substring(0, match.start) + replacement + str.substring(match.end);
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 46fa921..257679b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,15 +3,15 @@
# BSD-style license that can be found in the LICENSE file.
name: usage
-version: 3.4.2
+version: 4.0.0-nullsafety
description: A Google Analytics wrapper for command-line, web, and Flutter apps.
homepage: https://github.com/dart-lang/usage
environment:
- sdk: '>=2.2.0 <3.0.0'
+ sdk: '>=2.12.0-0 <3.0.0'
dependencies:
- path: ^1.4.0
+ path: ^1.8.0-nullsafety
dev_dependencies:
pedantic: ^1.9.0
diff --git a/test/src/common.dart b/test/src/common.dart
index bcdabf6..304faf9 100644
--- a/test/src/common.dart
+++ b/test/src/common.dart
@@ -9,7 +9,7 @@
import 'package:test/test.dart';
import 'package:usage/src/usage_impl.dart';
-AnalyticsImplMock createMock({Map<String, dynamic> props}) =>
+AnalyticsImplMock createMock({Map<String, dynamic>? props}) =>
AnalyticsImplMock('UA-0', props: props);
void was(Map m, String type) => expect(m['t'], type);
@@ -17,10 +17,10 @@
void hasnt(Map m, String key) => expect(m[key], isNull);
class AnalyticsImplMock extends AnalyticsImpl {
- MockProperties get mockProperties => properties;
- MockPostHandler get mockPostHandler => postHandler;
+ MockProperties get mockProperties => properties as MockProperties;
+ MockPostHandler get mockPostHandler => postHandler as MockPostHandler;
- AnalyticsImplMock(String trackingId, {Map<String, dynamic> props})
+ AnalyticsImplMock(String trackingId, {Map<String, dynamic>? props})
: super(trackingId, MockProperties(props), MockPostHandler(),
applicationName: 'Test App', applicationVersion: '0.1');
@@ -30,7 +30,7 @@
class MockProperties extends PersistentProperties {
Map<String, dynamic> props = {};
- MockProperties([Map<String, dynamic> props]) : super('mock') {
+ MockProperties([Map<String, dynamic>? props]) : super('mock') {
if (props != null) this.props.addAll(props);
}
diff --git a/test/usage_impl_io_test.dart b/test/usage_impl_io_test.dart
index 2fc117b..8b15606 100644
--- a/test/usage_impl_io_test.dart
+++ b/test/usage_impl_io_test.dart
@@ -53,7 +53,7 @@
class MockHttpClient implements HttpClient {
@override
- String userAgent;
+ String? userAgent;
int sendCount = 0;
int writeCount = 0;
bool closed = false;
@@ -73,7 +73,7 @@
MockHttpClientRequest(this.client);
@override
- void write(Object obj) {
+ void write(Object? obj) {
client.writeCount++;
}
@@ -93,7 +93,7 @@
MockHttpClientResponse(this.client);
@override
- Future<E> drain<E>([E futureValue]) {
+ Future<E> drain<E>([E? futureValue]) {
client.sendCount++;
return Future.value();
}
diff --git a/test/web_test.dart b/test/web_test.dart
index df2da92..eb0b107 100644
--- a/test/web_test.dart
+++ b/test/web_test.dart
@@ -59,7 +59,7 @@
class MockRequestor {
int sendCount = 0;
- Future<HttpRequest> request(String url, {String method, sendData}) {
+ Future<HttpRequest> request(String url, {String? method, sendData}) {
expect(url, isNotEmpty);
expect(method, isNotEmpty);
expect(sendData, isNotEmpty);