mirror of
https://github.com/darkmoonight/Rain.git
synced 2025-06-28 20:19:58 +00:00
Fix markers
This commit is contained in:
parent
8ed047a1aa
commit
383fb24881
4 changed files with 361 additions and 315 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -42,5 +42,3 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
/lib/secret_key.dart
|
|
|
@ -85,7 +85,9 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||||
child: ScaffoldMessenger(
|
child: ScaffoldMessenger(
|
||||||
key: globalKey,
|
key: globalKey,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: tabIndex == 2
|
||||||
|
? null
|
||||||
|
: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
leading: switch (tabIndex) {
|
leading: switch (tabIndex) {
|
||||||
|
@ -116,7 +118,8 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
optionsBuilder:
|
||||||
|
(TextEditingValue textEditingValue) {
|
||||||
if (textEditingValue.text.isEmpty) {
|
if (textEditingValue.text.isEmpty) {
|
||||||
return const Iterable<Result>.empty();
|
return const Iterable<Result>.empty();
|
||||||
}
|
}
|
||||||
|
@ -152,7 +155,8 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: options.length,
|
itemCount: options.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder:
|
||||||
|
(BuildContext context, int index) {
|
||||||
final Result option =
|
final Result option =
|
||||||
options.elementAt(index);
|
options.elementAt(index);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
|
@ -187,7 +191,9 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||||
: '$city' ', $district'
|
: '$city' ', $district'
|
||||||
: settings.location
|
: settings.location
|
||||||
? 'search'.tr
|
? 'search'.tr
|
||||||
: (isar.locationCaches.where().findAllSync())
|
: (isar.locationCaches
|
||||||
|
.where()
|
||||||
|
.findAllSync())
|
||||||
.isNotEmpty
|
.isNotEmpty
|
||||||
? 'loading'.tr
|
? 'loading'.tr
|
||||||
: 'searchCity'.tr,
|
: 'searchCity'.tr,
|
||||||
|
@ -199,10 +205,6 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||||
'cities'.tr,
|
'cities'.tr,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
),
|
),
|
||||||
2 => Text(
|
|
||||||
'map'.tr,
|
|
||||||
style: textStyle,
|
|
||||||
),
|
|
||||||
3 => Text(
|
3 => Text(
|
||||||
'settings_full'.tr,
|
'settings_full'.tr,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
||||||
|
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:flutter_map_animations/flutter_map_animations.dart';
|
import 'package:flutter_map_animations/flutter_map_animations.dart';
|
||||||
import 'package:flutter_map_cache/flutter_map_cache.dart';
|
import 'package:flutter_map_cache/flutter_map_cache.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:rain/app/controller/controller.dart';
|
import 'package:rain/app/controller/controller.dart';
|
||||||
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
import 'package:rain/app/data/weather.dart';
|
||||||
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
|
import 'package:rain/app/modules/cards/view/info_weather_card.dart';
|
||||||
import 'package:rain/app/modules/cards/widgets/create_card_weather.dart';
|
import 'package:rain/app/modules/cards/widgets/create_card_weather.dart';
|
||||||
import 'package:rain/secret_key.dart';
|
import 'package:rain/app/modules/cards/widgets/weather_card_container.dart';
|
||||||
|
import 'package:rain/app/widgets/status/status_weather.dart';
|
||||||
|
|
||||||
class MapWeather extends StatefulWidget {
|
class MapWeather extends StatefulWidget {
|
||||||
const MapWeather({super.key});
|
const MapWeather({super.key});
|
||||||
|
@ -21,56 +23,163 @@ class MapWeather extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MapWeatherState extends State<MapWeather> with TickerProviderStateMixin {
|
class _MapWeatherState extends State<MapWeather> with TickerProviderStateMixin {
|
||||||
late final _animatedMapController = AnimatedMapController(vsync: this);
|
late final AnimatedMapController _animatedMapController =
|
||||||
|
AnimatedMapController(vsync: this);
|
||||||
final weatherController = Get.put(WeatherController());
|
final weatherController = Get.put(WeatherController());
|
||||||
bool isDarkMode = Get.theme.brightness == Brightness.dark;
|
final statusWeather = StatusWeather();
|
||||||
|
|
||||||
final Future<CacheStore> _cacheStoreFuture = _getCacheStore();
|
final Future<CacheStore> _cacheStoreFuture = _getCacheStore();
|
||||||
|
|
||||||
|
final bool _isDarkMode = Get.theme.brightness == Brightness.dark;
|
||||||
|
WeatherCard? _selectedWeatherCard;
|
||||||
|
bool _isCardVisible = false;
|
||||||
|
double _cardBottomPosition = -200;
|
||||||
|
|
||||||
static Future<CacheStore> _getCacheStore() async {
|
static Future<CacheStore> _getCacheStore() async {
|
||||||
final dir = await getTemporaryDirectory();
|
final dir = await getTemporaryDirectory();
|
||||||
return FileCacheStore('${dir.path}${Platform.pathSeparator}MapTiles');
|
return FileCacheStore('${dir.path}${Platform.pathSeparator}MapTiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void _onMarkerTap(WeatherCard weatherCard) {
|
||||||
Widget build(BuildContext context) {
|
setState(() {
|
||||||
final mainLocation = weatherController.location;
|
_selectedWeatherCard = weatherCard;
|
||||||
// final mainWeather = weatherController.mainWeather;
|
_cardBottomPosition = 0;
|
||||||
// final weatherCard = WeatherCard.fromJson({}
|
_isCardVisible = true;
|
||||||
// ..addAll(mainWeather.toJson())
|
});
|
||||||
// ..addAll(mainLocation.toJson()));
|
}
|
||||||
|
|
||||||
Widget darkModeTilesContainerBuilder(
|
void _hideCard() {
|
||||||
BuildContext context,
|
setState(() {
|
||||||
Widget tilesContainer,
|
_cardBottomPosition = -200;
|
||||||
) {
|
});
|
||||||
return ColorFiltered(
|
Future.delayed(const Duration(milliseconds: 300), () {
|
||||||
colorFilter: const ColorFilter.matrix(<double>[
|
setState(() {
|
||||||
-0.2126, -0.7152, -0.0722, 0, 255, // Red channel
|
_isCardVisible = false;
|
||||||
-0.2126, -0.7152, -0.0722, 0, 255, // Green channel
|
});
|
||||||
-0.2126, -0.7152, -0.0722, 0, 255, // Blue channel
|
});
|
||||||
0, 0, 0, 1, 0, // Alpha channel
|
}
|
||||||
]),
|
|
||||||
child: tilesContainer,
|
Marker _buildMainLocationMarker(
|
||||||
|
WeatherCard weatherCard, int hourOfDay, int dayOfNow) {
|
||||||
|
return Marker(
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
point: LatLng(weatherCard.lat!, weatherCard.lon!),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => _onMarkerTap(weatherCard),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.theme.colorScheme.onSecondary,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Image.asset(
|
||||||
|
statusWeather.getImageNow(
|
||||||
|
weatherCard.weathercode![hourOfDay],
|
||||||
|
weatherCard.time![hourOfDay],
|
||||||
|
weatherCard.sunrise![dayOfNow],
|
||||||
|
weatherCard.sunset![dayOfNow],
|
||||||
|
),
|
||||||
|
scale: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget openStreetMapTileLayer(CacheStore cacheStore) {
|
Marker _buildCardMarker(WeatherCard weatherCardList) {
|
||||||
|
return Marker(
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
point: LatLng(weatherCardList.lat!, weatherCardList.lon!),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => _onMarkerTap(weatherCardList),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.theme.colorScheme.onSecondary,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Image.asset(
|
||||||
|
statusWeather.getImageNow(
|
||||||
|
weatherCardList.weathercode![weatherController.getTime(
|
||||||
|
weatherCardList.time!, weatherCardList.timezone!)],
|
||||||
|
weatherCardList.time![weatherController.getTime(
|
||||||
|
weatherCardList.time!, weatherCardList.timezone!)],
|
||||||
|
weatherCardList.sunrise![weatherController.getDay(
|
||||||
|
weatherCardList.timeDaily!, weatherCardList.timezone!)],
|
||||||
|
weatherCardList.sunset![weatherController.getDay(
|
||||||
|
weatherCardList.timeDaily!, weatherCardList.timezone!)],
|
||||||
|
),
|
||||||
|
scale: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMapTileLayer(CacheStore cacheStore) {
|
||||||
return TileLayer(
|
return TileLayer(
|
||||||
urlTemplate:
|
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
'https://api.mapbox.com/styles/v1/yoshimok/clzvnt6ae000s01qsh52veh8f/tiles/256/{z}/{x}/{y}@2x?access_token=$accessToken',
|
|
||||||
userAgentPackageName: 'com.darkmoonight.rain',
|
userAgentPackageName: 'com.darkmoonight.rain',
|
||||||
tileProvider: CachedTileProvider(
|
tileProvider: CachedTileProvider(
|
||||||
store: cacheStore,
|
store: cacheStore,
|
||||||
maxStale: const Duration(days: 30),
|
maxStale: const Duration(days: 30),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildWeatherCard() {
|
||||||
|
return AnimatedPositioned(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: _cardBottomPosition,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: _isCardVisible ? 1.0 : 0.0,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: _isCardVisible
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => InfoWeatherCard(weatherCard: _selectedWeatherCard!),
|
||||||
|
transition: Transition.downToUp,
|
||||||
|
),
|
||||||
|
child: WeatherCardContainer(
|
||||||
|
time: _selectedWeatherCard!.time!,
|
||||||
|
timeDaily: _selectedWeatherCard!.timeDaily!,
|
||||||
|
timeDay: _selectedWeatherCard!.sunrise!,
|
||||||
|
timeNight: _selectedWeatherCard!.sunset!,
|
||||||
|
weather: _selectedWeatherCard!.weathercode!,
|
||||||
|
degree: _selectedWeatherCard!.temperature2M!,
|
||||||
|
district: _selectedWeatherCard!.district!,
|
||||||
|
city: _selectedWeatherCard!.city!,
|
||||||
|
timezone: _selectedWeatherCard!.timezone!,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final mainLocation = weatherController.location;
|
||||||
|
final mainWeather = weatherController.mainWeather;
|
||||||
|
|
||||||
|
final hourOfDay = weatherController.hourOfDay.value;
|
||||||
|
final dayOfNow = weatherController.dayOfNow.value;
|
||||||
|
|
||||||
return FutureBuilder<CacheStore>(
|
return FutureBuilder<CacheStore>(
|
||||||
future: _cacheStoreFuture,
|
future: _cacheStoreFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(child: Text(snapshot.error.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
final cacheStore = snapshot.data!;
|
final cacheStore = snapshot.data!;
|
||||||
|
|
||||||
return FlutterMap(
|
return FlutterMap(
|
||||||
mapController: _animatedMapController.mapController,
|
mapController: _animatedMapController.mapController,
|
||||||
options: MapOptions(
|
options: MapOptions(
|
||||||
|
@ -83,6 +192,7 @@ class _MapWeatherState extends State<MapWeather> with TickerProviderStateMixin {
|
||||||
const LatLng(90, 180),
|
const LatLng(90, 180),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onTap: (_, __) => _hideCard(),
|
||||||
onLongPress: (tapPosition, point) => showModalBottomSheet(
|
onLongPress: (tapPosition, point) => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
|
@ -94,101 +204,50 @@ class _MapWeatherState extends State<MapWeather> with TickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
isDarkMode
|
if (_isDarkMode)
|
||||||
? darkModeTilesContainerBuilder(
|
ColorFiltered(
|
||||||
context, openStreetMapTileLayer(cacheStore))
|
colorFilter: const ColorFilter.matrix(<double>[
|
||||||
: openStreetMapTileLayer(cacheStore),
|
-0.2126, -0.7152, -0.0722, 0, 255, // Red channel
|
||||||
|
-0.2126, -0.7152, -0.0722, 0, 255, // Green channel
|
||||||
|
-0.2126, -0.7152, -0.0722, 0, 255, // Blue channel
|
||||||
|
0, 0, 0, 1, 0, // Alpha channel
|
||||||
|
]),
|
||||||
|
child: _buildMapTileLayer(cacheStore),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
_buildMapTileLayer(cacheStore),
|
||||||
RichAttributionWidget(
|
RichAttributionWidget(
|
||||||
animationConfig: const ScaleRAWA(),
|
animationConfig: const ScaleRAWA(),
|
||||||
attributions: [
|
attributions: [
|
||||||
TextSourceAttribution(
|
TextSourceAttribution(
|
||||||
'Mapbox contributors',
|
'OpenStreetMap contributors',
|
||||||
onTap: () => weatherController
|
onTap: () => weatherController
|
||||||
.urlLauncher('https://www.mapbox.com/legal/tos'),
|
.urlLauncher('https://openstreetmap.org/copyright'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(() {
|
||||||
() {
|
final mainMarker = _buildMainLocationMarker(
|
||||||
var weatherCards = weatherController.weatherCards.toList();
|
WeatherCard.fromJson({
|
||||||
|
...mainWeather.toJson(),
|
||||||
|
...mainLocation.toJson(),
|
||||||
|
}),
|
||||||
|
hourOfDay,
|
||||||
|
dayOfNow,
|
||||||
|
);
|
||||||
|
|
||||||
|
final cardMarkers = weatherController.weatherCards
|
||||||
|
.map((weatherCardList) => _buildCardMarker(weatherCardList))
|
||||||
|
.toList();
|
||||||
|
|
||||||
return MarkerLayer(
|
return MarkerLayer(
|
||||||
markers: [
|
markers: [mainMarker, ...cardMarkers],
|
||||||
Marker(
|
);
|
||||||
point: LatLng(mainLocation.lat!, mainLocation.lon!),
|
}),
|
||||||
child: GestureDetector(
|
_buildWeatherCard(),
|
||||||
onTap: () {},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
context.theme.colorScheme.secondaryContainer,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
IconsaxPlusBold.home,
|
|
||||||
color: context
|
|
||||||
.theme.colorScheme.onSecondaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
...weatherCards.map(
|
|
||||||
(weatherCardList) => Marker(
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
point: LatLng(
|
|
||||||
weatherCardList.lat!, weatherCardList.lon!),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context
|
|
||||||
.theme.colorScheme.secondaryContainer,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
IconsaxPlusBold.location,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
// Positioned(
|
|
||||||
// left: 0,
|
|
||||||
// right: 0,
|
|
||||||
// bottom: 0,
|
|
||||||
// child: GestureDetector(
|
|
||||||
// onTap: () => Get.to(
|
|
||||||
// () => InfoWeatherCard(
|
|
||||||
// weatherCard: weatherCard,
|
|
||||||
// ),
|
|
||||||
// transition: Transition.downToUp,
|
|
||||||
// ),
|
|
||||||
// child: WeatherCardContainer(
|
|
||||||
// time: mainWeather.time!,
|
|
||||||
// timeDaily: mainWeather.timeDaily!,
|
|
||||||
// timeDay: mainWeather.sunrise!,
|
|
||||||
// timeNight: mainWeather.sunset!,
|
|
||||||
// weather: mainWeather.weathercode!,
|
|
||||||
// degree: mainWeather.temperature2M!,
|
|
||||||
// district: mainLocation.district!,
|
|
||||||
// city: mainLocation.city!,
|
|
||||||
// timezone: mainWeather.timezone!,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
return Center(child: Text(snapshot.error.toString()));
|
|
||||||
}
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:rain/app/modules/settings/widgets/setting_card.dart';
|
||||||
import 'package:rain/main.dart';
|
import 'package:rain/main.dart';
|
||||||
import 'package:rain/theme/theme_controller.dart';
|
import 'package:rain/theme/theme_controller.dart';
|
||||||
import 'package:rain/utils/color_converter.dart';
|
import 'package:rain/utils/color_converter.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
@ -978,27 +977,15 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
icon: const Icon(LineAwesomeIcons.discord),
|
icon: const Icon(LineAwesomeIcons.discord),
|
||||||
text: 'Discord',
|
text: 'Discord',
|
||||||
onPressed: () async {
|
onPressed: () => weatherController.urlLauncher(
|
||||||
final Uri url = Uri.parse(
|
'https://discord.gg/JMMa9aHh8f'),
|
||||||
'https://discord.gg/JMMa9aHh8f');
|
|
||||||
if (!await launchUrl(url,
|
|
||||||
mode: LaunchMode.externalApplication)) {
|
|
||||||
throw Exception('Could not launch $url');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
SettingCard(
|
SettingCard(
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
icon: const Icon(LineAwesomeIcons.telegram),
|
icon: const Icon(LineAwesomeIcons.telegram),
|
||||||
text: 'Telegram',
|
text: 'Telegram',
|
||||||
onPressed: () async {
|
onPressed: () => weatherController
|
||||||
final Uri url =
|
.urlLauncher('https://t.me/darkmoonightX'),
|
||||||
Uri.parse('https://t.me/darkmoonightX');
|
|
||||||
if (!await launchUrl(url,
|
|
||||||
mode: LaunchMode.externalApplication)) {
|
|
||||||
throw Exception('Could not launch $url');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue