diff --git a/packages/url_launcher_watchos/CHANGELOG.md b/packages/url_launcher_watchos/CHANGELOG.md index aec7cb3..26a48a1 100644 --- a/packages/url_launcher_watchos/CHANGELOG.md +++ b/packages/url_launcher_watchos/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.1.2 + +* Example: `url_launcher`'s own example app and its official integration + test, unchanged, replace a hand-written demo (`AUTHORING.md` §3). The + former test stays beside it as `watchos_url_launcher_test.dart`. + ## 0.1.1 * README: the install snippet names `url_launcher: ^6.3.2` instead of `any`, diff --git a/packages/url_launcher_watchos/PORTING_REPORT.md b/packages/url_launcher_watchos/PORTING_REPORT.md index 19672fb..49fdf7a 100644 --- a/packages/url_launcher_watchos/PORTING_REPORT.md +++ b/packages/url_launcher_watchos/PORTING_REPORT.md @@ -9,8 +9,8 @@ | Host unit tests (`flutter-watchos test`) | ✅ pass (10) | | Native compile (`arm64-apple-watchos7.0-simulator`, `-Wall -Wextra`) | ✅ clean, all 3 symbols exported | | CLI integration | ✅ `url_launcher` drops off the "no watchOS implementation" warning in a real app | -| Upstream integration test | ✗ not reused — upstream asserts an in-app `SFSafariViewController`/webview surface that cannot exist on watchOS | -| Own integration test (Simulator, real FFI) | ✅ pass (5) — symbols resolve via `DynamicLibrary.process()` and `url_launcher` federates to this implementation | +| Upstream integration test | ✅ passes verbatim on the watchOS 27.0 Simulator (`url_launcher`'s `url_launcher_test.dart`, 2026-10-24) | +| watchOS integration test (`watchos_url_launcher_test.dart`, Simulator, real FFI) | ✅ pass (6) — symbols resolve via `DynamicLibrary.process()` and `url_launcher` federates to this implementation | | On-device (Apple Watch Ultra 3, watchOS 26.5, release AOT) | ✅ `tel:` and `sms:` confirmed by hand — both raise the system call / compose UI on the watch. Web path confirmed too: the system sheet appears and reports the link can be viewed on the iPhone | | On-device Handoff pickup on the phone | ✅ confirmed — the Handoff icon appears in the paired iPhone's app switcher and opens the page | @@ -29,6 +29,14 @@ scaffold's shape (package name, `dartPluginClass: UrlLauncherWatchos`, `ffiPlugin: true`, `implements: url_launcher`) matches this package; the exported symbols and every method body are the human half it leaves behind. +## Example + +`url_launcher`'s own example app and its official integration test, ported +unchanged with `flutter-watchos plugin port --include-example`. There are no +watchOS deviations. The official test checks `canLaunchUrl` and the legacy +`canLaunch`. `watchos_url_launcher_test.dart` beside it checks the on-watch +browser, Handoff and the refused schemes. + ## Status ✅ WORKING FFI implementation: diff --git a/packages/url_launcher_watchos/README.md b/packages/url_launcher_watchos/README.md index 15d525b..c10540c 100644 --- a/packages/url_launcher_watchos/README.md +++ b/packages/url_launcher_watchos/README.md @@ -83,6 +83,15 @@ These members of `url_launcher_platform_interface` 2.3.2 throw or fail on watchO | `launchUrl` / `launch` with a scheme other than `http:`, `https:`, `tel:` and `sms:` (`mailto:` included) | return false | `openSystemURL:` owns only `tel:` and `sms:` and fails silently on other schemes | | `supportsMode(externalNonBrowserApplication)` | returns false | the watch cannot tell whether the iPhone opens a web URL in an app; `launchUrl` still launches it as `externalApplication` | +## Example + +The example is `url_launcher`'s own example app, and +`example/integration_test/url_launcher_test.dart` is its official test, both +unchanged, ported with `flutter-watchos plugin port --include-example`. There +are no watchOS deviations. `example/integration_test/watchos_url_launcher_test.dart` +covers what the official test does not: the on-watch browser, Handoff and +the schemes the watch refuses. + ## Testing `UrlLauncherWatchos.bindingsOverride` accepts a fake extending diff --git a/packages/url_launcher_watchos/example/integration_test/url_launcher_test.dart b/packages/url_launcher_watchos/example/integration_test/url_launcher_test.dart index 65fe73c..345a892 100644 --- a/packages/url_launcher_watchos/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher_watchos/example/integration_test/url_launcher_test.dart @@ -1,16 +1,10 @@ -// Copyright 2026 The FlutterWatch Authors. All rights reserved. +// Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// -// Runs on a real watch (or the Simulator) against the linked FFI symbols, so -// it proves the native side is present and answering — something the host -// unit tests, which fake the bindings, cannot. -// -// What it deliberately does NOT assert: that a URL actually opened. Neither -// the on-watch browser, `openSystemURL:` nor Handoff reports completion to the -// app, and the watch has no API to observe the paired phone. Those need a -// human with a wrist. +import 'dart:io' show Platform; + +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -18,53 +12,24 @@ import 'package:url_launcher/url_launcher.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('canLaunchUrl (real FFI)', () { - test('accepts the schemes watchOS can act on', () async { - expect(await canLaunchUrl(Uri.parse('tel:+15551234567')), isTrue); - expect(await canLaunchUrl(Uri.parse('sms:+15551234567')), isTrue); - expect(await canLaunchUrl(Uri.parse('https://flutterwatch.dev')), isTrue); - expect(await canLaunchUrl(Uri.parse('http://example.com')), isTrue); - }); - - test('refuses schemes with no watchOS mechanism', () async { - expect( - await canLaunchUrl(Uri.parse('mailto:hello@example.com')), isFalse); - expect(await canLaunchUrl(Uri.parse('ftp://example.com')), isFalse); - }); - }); - - group('launchUrl (real FFI)', () { - test('a web URL is accepted for the on-watch browser', () async { - // True means "handed to ASWebAuthenticationSession", not "rendered". - expect(await launchUrl(Uri.parse('https://flutterwatch.dev')), isTrue); - await closeInAppWebView(); - }); - - test('a web URL is accepted for Handoff with externalApplication', - () async { - // True means "published as an NSUserActivity", not "opened". - expect( - await launchUrl( - Uri.parse('https://flutterwatch.dev'), - mode: LaunchMode.externalApplication, - ), - isTrue, - ); - }); - - test('an unsupported scheme is refused, not silently swallowed', () async { - // Reports false rather than throwing: the caller gets a definite "this - // did not happen" instead of an exception to catch. - expect(await launchUrl(Uri.parse('ftp://example.com')), isFalse); - }); - - test('closeWebView withdraws the Handoff activity without throwing', - () async { - await launchUrl( - Uri.parse('https://flutterwatch.dev'), - mode: LaunchMode.externalApplication, - ); - await closeInAppWebView(); - }); + testWidgets('canLaunch', (WidgetTester _) async { + expect( + await canLaunchUrl(Uri(scheme: 'randomscheme', path: 'a_path')), false); + + // Generally all devices should have some default browser. + expect(await canLaunchUrl(Uri(scheme: 'http', host: 'flutter.dev')), true); + expect(await canLaunchUrl(Uri(scheme: 'https', host: 'flutter.dev')), true); + + // SMS handling is available by default on most platforms. + if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) { + expect(await canLaunchUrl(Uri(scheme: 'sms', path: '5555555555')), true); + } + + // Sanity-check legacy API. + // ignore: deprecated_member_use + expect(await canLaunch('randomstring'), false); + // Generally all devices should have some default browser. + // ignore: deprecated_member_use + expect(await canLaunch('https://flutter.dev'), true); }); } diff --git a/packages/url_launcher_watchos/example/integration_test/watchos_url_launcher_test.dart b/packages/url_launcher_watchos/example/integration_test/watchos_url_launcher_test.dart new file mode 100644 index 0000000..65fe73c --- /dev/null +++ b/packages/url_launcher_watchos/example/integration_test/watchos_url_launcher_test.dart @@ -0,0 +1,70 @@ +// Copyright 2026 The FlutterWatch Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Runs on a real watch (or the Simulator) against the linked FFI symbols, so +// it proves the native side is present and answering — something the host +// unit tests, which fake the bindings, cannot. +// +// What it deliberately does NOT assert: that a URL actually opened. Neither +// the on-watch browser, `openSystemURL:` nor Handoff reports completion to the +// app, and the watch has no API to observe the paired phone. Those need a +// human with a wrist. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:url_launcher/url_launcher.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('canLaunchUrl (real FFI)', () { + test('accepts the schemes watchOS can act on', () async { + expect(await canLaunchUrl(Uri.parse('tel:+15551234567')), isTrue); + expect(await canLaunchUrl(Uri.parse('sms:+15551234567')), isTrue); + expect(await canLaunchUrl(Uri.parse('https://flutterwatch.dev')), isTrue); + expect(await canLaunchUrl(Uri.parse('http://example.com')), isTrue); + }); + + test('refuses schemes with no watchOS mechanism', () async { + expect( + await canLaunchUrl(Uri.parse('mailto:hello@example.com')), isFalse); + expect(await canLaunchUrl(Uri.parse('ftp://example.com')), isFalse); + }); + }); + + group('launchUrl (real FFI)', () { + test('a web URL is accepted for the on-watch browser', () async { + // True means "handed to ASWebAuthenticationSession", not "rendered". + expect(await launchUrl(Uri.parse('https://flutterwatch.dev')), isTrue); + await closeInAppWebView(); + }); + + test('a web URL is accepted for Handoff with externalApplication', + () async { + // True means "published as an NSUserActivity", not "opened". + expect( + await launchUrl( + Uri.parse('https://flutterwatch.dev'), + mode: LaunchMode.externalApplication, + ), + isTrue, + ); + }); + + test('an unsupported scheme is refused, not silently swallowed', () async { + // Reports false rather than throwing: the caller gets a definite "this + // did not happen" instead of an exception to catch. + expect(await launchUrl(Uri.parse('ftp://example.com')), isFalse); + }); + + test('closeWebView withdraws the Handoff activity without throwing', + () async { + await launchUrl( + Uri.parse('https://flutterwatch.dev'), + mode: LaunchMode.externalApplication, + ); + await closeInAppWebView(); + }); + }); +} diff --git a/packages/url_launcher_watchos/example/lib/basic.dart b/packages/url_launcher_watchos/example/lib/basic.dart new file mode 100644 index 0000000..4e3aac0 --- /dev/null +++ b/packages/url_launcher_watchos/example/lib/basic.dart @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Run this example with: flutter run -t lib/basic.dart -d emulator + +// This file is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. + +// #docregion basic-example +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +final Uri _url = Uri.parse('https://flutter.dev'); + +void main() => runApp( + const MaterialApp( + home: Material( + child: Center( + child: ElevatedButton( + onPressed: _launchUrl, child: Text('Show Flutter homepage')), + ), + ), + ), + ); + +Future _launchUrl() async { + if (!await launchUrl(_url)) { + throw Exception('Could not launch $_url'); + } +} + +// #enddocregion basic-example diff --git a/packages/url_launcher_watchos/example/lib/encoding.dart b/packages/url_launcher_watchos/example/lib/encoding.dart new file mode 100644 index 0000000..8f8e9e6 --- /dev/null +++ b/packages/url_launcher_watchos/example/lib/encoding.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Run this example with: flutter run -t lib/encoding.dart -d emulator + +// This file is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. + +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// Encode [params] so it produces a correct query string. +/// Workaround for: https://github.com/dart-lang/sdk/issues/43838 +// #docregion encode-query-parameters +String? encodeQueryParameters(Map params) { + return params.entries + .map( + (MapEntry e) => + '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}', + ) + .join('&'); +} + +// #enddocregion encode-query-parameters + +void main() => runApp( + const MaterialApp( + home: Material( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: _composeMail, child: Text('Compose an email')), + ], + ), + ), + ), + ); + +void _composeMail() { + // #docregion encode-query-parameters + final emailLaunchUri = Uri( + scheme: 'mailto', + path: 'smith@example.com', + query: encodeQueryParameters({ + 'subject': 'Example Subject & Symbols are allowed!', + }), + ); + + launchUrl(emailLaunchUri); + // #enddocregion encode-query-parameters +} diff --git a/packages/url_launcher_watchos/example/lib/files.dart b/packages/url_launcher_watchos/example/lib/files.dart new file mode 100644 index 0000000..299c1b2 --- /dev/null +++ b/packages/url_launcher_watchos/example/lib/files.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Run this example with: flutter run -t lib/files.dart -d linux + +// This file is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:path/path.dart' as p; +import 'package:url_launcher/url_launcher.dart'; + +void main() => runApp( + const MaterialApp( + home: Material( + child: Center( + child: + ElevatedButton(onPressed: _openFile, child: Text('Open File')), + ), + ), + ), + ); + +Future _openFile() async { + // Prepare a file within tmp + final String tempFilePath = p.joinAll([ + ...p.split(Directory.systemTemp.path), + 'flutter_url_launcher_example.txt', + ]); + final testFile = File(tempFilePath); + await testFile.writeAsString('Hello, world!'); + // #docregion file + final String filePath = testFile.absolute.path; + final uri = Uri.file(filePath); + + if (!File(uri.toFilePath()).existsSync()) { + throw Exception('$uri does not exist!'); + } + if (!await launchUrl(uri)) { + throw Exception('Could not launch $uri'); + } + // #enddocregion file +} diff --git a/packages/url_launcher_watchos/example/lib/main.dart b/packages/url_launcher_watchos/example/lib/main.dart index 779e3fe..a4bcf52 100644 --- a/packages/url_launcher_watchos/example/lib/main.dart +++ b/packages/url_launcher_watchos/example/lib/main.dart @@ -1,85 +1,250 @@ -// Copyright 2026 The FlutterWatch Authors. All rights reserved. +// Copyright 2013 The Flutter Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; -void main() => runApp(const ExampleApp()); +void main() { + runApp(const MyApp()); +} -/// Exercises each class of URL watchOS can act on, and one it cannot. -class ExampleApp extends StatefulWidget { - const ExampleApp({super.key}); +class MyApp extends StatelessWidget { + const MyApp({super.key}); @override - State createState() => _ExampleAppState(); + Widget build(BuildContext context) { + return MaterialApp( + title: 'URL Launcher', + theme: ThemeData(primarySwatch: Colors.blue), + home: const MyHomePage(title: 'URL Launcher'), + ); + } } -class _ExampleAppState extends State { - String _status = 'Pick a URL'; +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + bool _hasCallSupport = false; + Future? _launched; + String _phone = ''; + + @override + void initState() { + super.initState(); + // Check for phone call support. + canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) { + setState(() { + _hasCallSupport = result; + }); + }); + } + + Future _launchInBrowser(Uri url) async { + if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { + throw Exception('Could not launch $url'); + } + } + + Future _launchInBrowserView(Uri url) async { + if (!await launchUrl(url, mode: LaunchMode.inAppBrowserView)) { + throw Exception('Could not launch $url'); + } + } + + Future _launchInWebView(Uri url) async { + if (!await launchUrl(url, mode: LaunchMode.inAppWebView)) { + throw Exception('Could not launch $url'); + } + } + + Future _launchInAppWithBrowserOptions(Uri url) async { + if (!await launchUrl( + url, + mode: LaunchMode.inAppBrowserView, + browserConfiguration: const BrowserConfiguration(showTitle: true), + )) { + throw Exception('Could not launch $url'); + } + } + + Future _launchAsInAppWebViewWithCustomHeaders(Uri url) async { + if (!await launchUrl( + url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'my_header_key': 'my_header_value'}, + ), + )) { + throw Exception('Could not launch $url'); + } + } + + Future _launchInWebViewWithoutJavaScript(Uri url) async { + if (!await launchUrl( + url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableJavaScript: false), + )) { + throw Exception('Could not launch $url'); + } + } - Future _open(String url, - {LaunchMode mode = LaunchMode.platformDefault}) async { - final Uri uri = Uri.parse(url); - if (!await canLaunchUrl(uri)) { - setState(() => _status = 'unsupported: ${uri.scheme}:'); - return; + Future _launchInWebViewWithoutDomStorage(Uri url) async { + if (!await launchUrl( + url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableDomStorage: false), + )) { + throw Exception('Could not launch $url'); } - final bool ok = await launchUrl(uri, mode: mode); - if (!mounted) { - return; + } + + Future _launchUniversalLinkIOS(Uri url) async { + final bool nativeAppLaunchSucceeded = await launchUrl( + url, + mode: LaunchMode.externalNonBrowserApplication, + ); + if (!nativeAppLaunchSucceeded) { + await launchUrl(url, mode: LaunchMode.inAppBrowserView); } - // "Handed to the system", not "the user followed it": watchOS reports no - // completion for any of these. - setState(() => _status = ok ? 'handed to system' : 'refused'); + } + + Widget _launchStatus(BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return const Text(''); + } + } + + Future _makePhoneCall(String phoneNumber) async { + final launchUri = Uri(scheme: 'tel', path: phoneNumber); + await launchUrl(launchUri); } @override Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - body: SafeArea( - child: ListView( - padding: const EdgeInsets.all(8), + // onPressed calls using this URL are not gated on a 'canLaunch' check + // because the assumption is that every device can launch a web URL. + final toLaunch = + Uri(scheme: 'https', host: 'www.cylog.org', path: 'headers/'); + return Scaffold( + appBar: AppBar(title: Text(widget.title)), + body: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(_status, textAlign: TextAlign.center), - const SizedBox(height: 8), - _Button( - label: 'https (on watch)', - onTap: () => _open('https://example.com'), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + onChanged: (String text) => _phone = text, + decoration: const InputDecoration( + hintText: 'Input the phone number to launch'), + ), + ), + ElevatedButton( + onPressed: _hasCallSupport + ? () => setState(() { + _launched = _makePhoneCall(_phone); + }) + : null, + child: _hasCallSupport + ? const Text('Make phone call') + : const Text('Calling not supported'), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text(toLaunch.toString())), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInBrowser(toLaunch); + }), + child: const Text('Launch in browser'), ), - _Button( - label: 'https (→ iPhone)', - onTap: () => _open( - 'https://example.com', - mode: LaunchMode.externalApplication, + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInBrowserView(toLaunch); + }), + child: const Text('Launch in app'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchAsInAppWebViewWithCustomHeaders(toLaunch); + }), + child: const Text('Launch in app (Custom Headers)'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithoutJavaScript(toLaunch); + }), + child: const Text('Launch in app (JavaScript OFF)'), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithoutDomStorage(toLaunch); + }), + child: const Text('Launch in app (DOM storage OFF)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchUniversalLinkIOS(toLaunch); + }), + child: const Text( + 'Launch a universal link in a native app, fallback to Safari.(Youtube)', ), ), - _Button(label: 'tel', onTap: () => _open('tel:+15551234567')), - _Button(label: 'sms', onTap: () => _open('sms:+15551234567')), - _Button( - label: 'mailto (unsupported)', - onTap: () => _open('mailto:hello@example.com'), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInWebView(toLaunch); + Timer(const Duration(seconds: 5), () { + closeInAppWebView(); + }); + }), + child: const Text('Launch in app + close after 5 seconds'), ), + const Padding(padding: EdgeInsets.all(16.0)), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInAppWithBrowserOptions(toLaunch); + }), + child: const Text('Launch in app with title displayed'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + Link( + uri: Uri.parse( + 'https://pub.dev/documentation/url_launcher/latest/link/link-library.html', + ), + target: LinkTarget.blank, + builder: (BuildContext ctx, FollowLink? openLink) { + return TextButton.icon( + onPressed: openLink, + label: const Text('Link Widget documentation'), + icon: const Icon(Icons.read_more), + ); + }, + ), + const Padding(padding: EdgeInsets.all(16.0)), + FutureBuilder(future: _launched, builder: _launchStatus), ], ), - ), + ], ), ); } } - -class _Button extends StatelessWidget { - const _Button({required this.label, required this.onTap}); - - final String label; - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 3), - child: ElevatedButton(onPressed: onTap, child: Text(label)), - ); - } -} diff --git a/packages/url_launcher_watchos/example/pubspec.yaml b/packages/url_launcher_watchos/example/pubspec.yaml index 0c58859..f80585c 100644 --- a/packages/url_launcher_watchos/example/pubspec.yaml +++ b/packages/url_launcher_watchos/example/pubspec.yaml @@ -12,6 +12,8 @@ dependencies: url_launcher: ^6.3.2 url_launcher_watchos: path: ../ + # Upstream's example imports it (lib/files.dart). + path: ^1.8.0 dev_dependencies: flutter_test: diff --git a/packages/url_launcher_watchos/pubspec.yaml b/packages/url_launcher_watchos/pubspec.yaml index f09a564..e2bd33a 100644 --- a/packages/url_launcher_watchos/pubspec.yaml +++ b/packages/url_launcher_watchos/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_watchos description: "watchOS implementation of url_launcher over dart:ffi: web pages in the watch's system browser, tel: and sms: through the system handler, and web links handed to the iPhone." -version: 0.1.1 +version: 0.1.2 homepage: https://flutterwatch.dev repository: https://github.com/flutterwatch/plugins/tree/main/packages/url_launcher_watchos issue_tracker: https://github.com/flutterwatch/plugins/issues