diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index c103261..d136543 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -3,7 +3,7 @@ "notfoundinClipboard": "No links detected on clipboard", "fromClipboard": "Import from clipboard", "inputbyKeyboard": "Import from plain text", - "notfound": "Not found", + "notfound": "Not Found", "addConfig": "Create configuration", "submitNewRules": "Submit new rules", "whichSupport": "View supported rules", diff --git a/lib/main.dart b/lib/main.dart index d3fb1e8..de64503 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:oktoast/oktoast.dart'; import 'package:rssaid/shared_prefs.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:rssaid/theme.dart'; import 'package:rssaid/views/home.dart'; Future main() async { @@ -38,10 +39,8 @@ class RSSAidApp extends StatelessWidget { Locale('en', ''), // English, no country code Locale('zh', ''), // Chinese, no country code ], - theme: ThemeData( - useMaterial3: true, - brightness: Brightness.light, //指定亮度主题,有白色/黑色两种可选。 - ), //这里我们选浅蓝色为强调色值。 + theme: MaterialTheme(Theme.of(context).textTheme).light(), + darkTheme: MaterialTheme(Theme.of(context).textTheme).dark(), home: HomePage(), )); } diff --git a/lib/theme.dart b/lib/theme.dart new file mode 100644 index 0000000..1d8f0c4 --- /dev/null +++ b/lib/theme.dart @@ -0,0 +1,389 @@ +import "package:flutter/material.dart"; + +class MaterialTheme { + final TextTheme textTheme; + + const MaterialTheme(this.textTheme); + + static ColorScheme lightScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(4282149177), + surfaceTint: Color(4282149177), + onPrimary: Color(4294967295), + primaryContainer: Color(4290572468), + onPrimaryContainer: Color(4278198788), + secondary: Color(4285030163), + onSecondary: Color(4294967295), + secondaryContainer: Color(4294043018), + onSecondaryContainer: Color(4280228864), + tertiary: Color(4284831119), + onTertiary: Color(4294967295), + tertiaryContainer: Color(4293516799), + onTertiaryContainer: Color(4280291399), + error: Color(4290386458), + onError: Color(4294967295), + errorContainer: Color(4294957782), + onErrorContainer: Color(4282449922), + surface: Color(4294441969), + onSurface: Color(4279835927), + onSurfaceVariant: Color(4282534208), + outline: Color(4285692271), + outlineVariant: Color(4290955709), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4281152044), + inversePrimary: Color(4288795546), + primaryFixed: Color(4290572468), + onPrimaryFixed: Color(4278198788), + primaryFixedDim: Color(4288795546), + onPrimaryFixedVariant: Color(4280569892), + secondaryFixed: Color(4294043018), + onSecondaryFixed: Color(4280228864), + secondaryFixedDim: Color(4292135025), + onSecondaryFixedVariant: Color(4283385856), + tertiaryFixed: Color(4293516799), + onTertiaryFixed: Color(4280291399), + tertiaryFixedDim: Color(4291804670), + onTertiaryFixedVariant: Color(4283252085), + surfaceDim: Color(4292402130), + surfaceBright: Color(4294441969), + surfaceContainerLowest: Color(4294967295), + surfaceContainerLow: Color(4294047211), + surfaceContainer: Color(4293717990), + surfaceContainerHigh: Color(4293323232), + surfaceContainerHighest: Color(4292928731), + ); + } + + ThemeData light() { + return theme(lightScheme()); + } + + static ColorScheme lightMediumContrastScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(4280306720), + surfaceTint: Color(4282149177), + onPrimary: Color(4294967295), + primaryContainer: Color(4283531085), + onPrimaryContainer: Color(4294967295), + secondary: Color(4283057152), + onSecondary: Color(4294967295), + secondaryContainer: Color(4286543400), + onSecondaryContainer: Color(4294967295), + tertiary: Color(4282988913), + onTertiary: Color(4294967295), + tertiaryContainer: Color(4286344103), + onTertiaryContainer: Color(4294967295), + error: Color(4287365129), + onError: Color(4294967295), + errorContainer: Color(4292490286), + onErrorContainer: Color(4294967295), + surface: Color(4294441969), + onSurface: Color(4279835927), + onSurfaceVariant: Color(4282271036), + outline: Color(4284113239), + outlineVariant: Color(4285955442), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4281152044), + inversePrimary: Color(4288795546), + primaryFixed: Color(4283531085), + onPrimaryFixed: Color(4294967295), + primaryFixedDim: Color(4281951799), + onPrimaryFixedVariant: Color(4294967295), + secondaryFixed: Color(4286543400), + onSecondaryFixed: Color(4294967295), + secondaryFixedDim: Color(4284833040), + onSecondaryFixedVariant: Color(4294967295), + tertiaryFixed: Color(4286344103), + onTertiaryFixed: Color(4294967295), + tertiaryFixedDim: Color(4284633996), + onTertiaryFixedVariant: Color(4294967295), + surfaceDim: Color(4292402130), + surfaceBright: Color(4294441969), + surfaceContainerLowest: Color(4294967295), + surfaceContainerLow: Color(4294047211), + surfaceContainer: Color(4293717990), + surfaceContainerHigh: Color(4293323232), + surfaceContainerHighest: Color(4292928731), + ); + } + + ThemeData lightMediumContrast() { + return theme(lightMediumContrastScheme()); + } + + static ColorScheme lightHighContrastScheme() { + return const ColorScheme( + brightness: Brightness.light, + primary: Color(4278200581), + surfaceTint: Color(4282149177), + onPrimary: Color(4294967295), + primaryContainer: Color(4280306720), + onPrimaryContainer: Color(4294967295), + secondary: Color(4280754944), + onSecondary: Color(4294967295), + secondaryContainer: Color(4283057152), + onSecondaryContainer: Color(4294967295), + tertiary: Color(4280751950), + onTertiary: Color(4294967295), + tertiaryContainer: Color(4282988913), + onTertiaryContainer: Color(4294967295), + error: Color(4283301890), + onError: Color(4294967295), + errorContainer: Color(4287365129), + onErrorContainer: Color(4294967295), + surface: Color(4294441969), + onSurface: Color(4278190080), + onSurfaceVariant: Color(4280231454), + outline: Color(4282271036), + outlineVariant: Color(4282271036), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4281152044), + inversePrimary: Color(4291230397), + primaryFixed: Color(4280306720), + onPrimaryFixed: Color(4294967295), + primaryFixedDim: Color(4278596875), + onPrimaryFixedVariant: Color(4294967295), + secondaryFixed: Color(4283057152), + onSecondaryFixed: Color(4294967295), + secondaryFixedDim: Color(4281478400), + onSecondaryFixedVariant: Color(4294967295), + tertiaryFixed: Color(4282988913), + onTertiaryFixed: Color(4294967295), + tertiaryFixedDim: Color(4281475929), + onTertiaryFixedVariant: Color(4294967295), + surfaceDim: Color(4292402130), + surfaceBright: Color(4294441969), + surfaceContainerLowest: Color(4294967295), + surfaceContainerLow: Color(4294047211), + surfaceContainer: Color(4293717990), + surfaceContainerHigh: Color(4293323232), + surfaceContainerHighest: Color(4292928731), + ); + } + + ThemeData lightHighContrast() { + return theme(lightHighContrastScheme()); + } + + static ColorScheme darkScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(4288795546), + surfaceTint: Color(4288795546), + onPrimary: Color(4278860047), + primaryContainer: Color(4280569892), + onPrimaryContainer: Color(4290572468), + secondary: Color(4292135025), + onSecondary: Color(4281741568), + secondaryContainer: Color(4283385856), + onSecondaryContainer: Color(4294043018), + tertiary: Color(4291804670), + onTertiary: Color(4281739101), + tertiaryContainer: Color(4283252085), + onTertiaryContainer: Color(4293516799), + error: Color(4294948011), + onError: Color(4285071365), + errorContainer: Color(4287823882), + onErrorContainer: Color(4294957782), + surface: Color(4279243791), + onSurface: Color(4292928731), + onSurfaceVariant: Color(4290955709), + outline: Color(4287402888), + outlineVariant: Color(4282534208), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4292928731), + inversePrimary: Color(4282149177), + primaryFixed: Color(4290572468), + onPrimaryFixed: Color(4278198788), + primaryFixedDim: Color(4288795546), + onPrimaryFixedVariant: Color(4280569892), + secondaryFixed: Color(4294043018), + onSecondaryFixed: Color(4280228864), + secondaryFixedDim: Color(4292135025), + onSecondaryFixedVariant: Color(4283385856), + tertiaryFixed: Color(4293516799), + onTertiaryFixed: Color(4280291399), + tertiaryFixedDim: Color(4291804670), + onTertiaryFixedVariant: Color(4283252085), + surfaceDim: Color(4279243791), + surfaceBright: Color(4281743924), + surfaceContainerLowest: Color(4278914826), + surfaceContainerLow: Color(4279835927), + surfaceContainer: Color(4280099099), + surfaceContainerHigh: Color(4280757029), + surfaceContainerHighest: Color(4281480752), + ); + } + + ThemeData dark() { + return theme(darkScheme()); + } + + static ColorScheme darkMediumContrastScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(4289058973), + surfaceTint: Color(4288795546), + onPrimary: Color(4278197251), + primaryContainer: Color(4285373543), + onPrimaryContainer: Color(4278190080), + secondary: Color(4292398453), + onSecondary: Color(4279899904), + secondaryContainer: Color(4288451138), + onSecondaryContainer: Color(4278190080), + tertiary: Color(4292067839), + onTertiary: Color(4279961922), + tertiaryContainer: Color(4288186309), + onTertiaryContainer: Color(4278190080), + error: Color(4294949553), + onError: Color(4281794561), + errorContainer: Color(4294923337), + onErrorContainer: Color(4278190080), + surface: Color(4279243791), + onSurface: Color(4294573299), + onSurfaceVariant: Color(4291218881), + outline: Color(4288587162), + outlineVariant: Color(4286481787), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4292928731), + inversePrimary: Color(4280635685), + primaryFixed: Color(4290572468), + onPrimaryFixed: Color(4278195714), + primaryFixedDim: Color(4288795546), + onPrimaryFixedVariant: Color(4279385876), + secondaryFixed: Color(4294043018), + onSecondaryFixed: Color(4279505152), + secondaryFixedDim: Color(4292135025), + onSecondaryFixedVariant: Color(4282201856), + tertiaryFixed: Color(4293516799), + onTertiaryFixed: Color(4279632701), + tertiaryFixedDim: Color(4291804670), + onTertiaryFixedVariant: Color(4282133603), + surfaceDim: Color(4279243791), + surfaceBright: Color(4281743924), + surfaceContainerLowest: Color(4278914826), + surfaceContainerLow: Color(4279835927), + surfaceContainer: Color(4280099099), + surfaceContainerHigh: Color(4280757029), + surfaceContainerHighest: Color(4281480752), + ); + } + + ThemeData darkMediumContrast() { + return theme(darkMediumContrastScheme()); + } + + static ColorScheme darkHighContrastScheme() { + return const ColorScheme( + brightness: Brightness.dark, + primary: Color(4294049770), + surfaceTint: Color(4288795546), + onPrimary: Color(4278190080), + primaryContainer: Color(4289058973), + onPrimaryContainer: Color(4278190080), + secondary: Color(4294966002), + onSecondary: Color(4278190080), + secondaryContainer: Color(4292398453), + onSecondaryContainer: Color(4278190080), + tertiary: Color(4294965759), + onTertiary: Color(4278190080), + tertiaryContainer: Color(4292067839), + onTertiaryContainer: Color(4278190080), + error: Color(4294965753), + onError: Color(4278190080), + errorContainer: Color(4294949553), + onErrorContainer: Color(4278190080), + surface: Color(4279243791), + onSurface: Color(4294967295), + onSurfaceVariant: Color(4294442480), + outline: Color(4291218881), + outlineVariant: Color(4291218881), + shadow: Color(4278190080), + scrim: Color(4278190080), + inverseSurface: Color(4292928731), + inversePrimary: Color(4278399497), + primaryFixed: Color(4290901176), + onPrimaryFixed: Color(4278190080), + primaryFixedDim: Color(4289058973), + onPrimaryFixedVariant: Color(4278197251), + secondaryFixed: Color(4294306190), + onSecondaryFixed: Color(4278190080), + secondaryFixedDim: Color(4292398453), + onSecondaryFixedVariant: Color(4279899904), + tertiaryFixed: Color(4293780223), + onTertiaryFixed: Color(4278190080), + tertiaryFixedDim: Color(4292067839), + onTertiaryFixedVariant: Color(4279961922), + surfaceDim: Color(4279243791), + surfaceBright: Color(4281743924), + surfaceContainerLowest: Color(4278914826), + surfaceContainerLow: Color(4279835927), + surfaceContainer: Color(4280099099), + surfaceContainerHigh: Color(4280757029), + surfaceContainerHighest: Color(4281480752), + ); + } + + ThemeData darkHighContrast() { + return theme(darkHighContrastScheme()); + } + + + ThemeData theme(ColorScheme colorScheme) => ThemeData( + useMaterial3: true, + brightness: colorScheme.brightness, + colorScheme: colorScheme, + textTheme: textTheme.apply( + bodyColor: colorScheme.onSurface, + displayColor: colorScheme.onSurface, + ), + scaffoldBackgroundColor: colorScheme.background, + canvasColor: colorScheme.surface, + ); + + + List get extendedColors => [ + ]; +} + +class ExtendedColor { + final Color seed, value; + final ColorFamily light; + final ColorFamily lightHighContrast; + final ColorFamily lightMediumContrast; + final ColorFamily dark; + final ColorFamily darkHighContrast; + final ColorFamily darkMediumContrast; + + const ExtendedColor({ + required this.seed, + required this.value, + required this.light, + required this.lightHighContrast, + required this.lightMediumContrast, + required this.dark, + required this.darkHighContrast, + required this.darkMediumContrast, + }); +} + +class ColorFamily { + const ColorFamily({ + required this.color, + required this.onColor, + required this.colorContainer, + required this.onColorContainer, + }); + + final Color color; + final Color onColor; + final Color colorContainer; + final Color onColorContainer; +} diff --git a/lib/views/components/not_found.dart b/lib/views/components/not_found.dart new file mode 100644 index 0000000..2f610bd --- /dev/null +++ b/lib/views/components/not_found.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:rssaid/common/common.dart'; + + +class NotFound extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('assets/imgs/404.png'), + Text( + AppLocalizations.of(context)!.notfound, + style: TextStyle(fontSize: 24, + fontWeight: FontWeight.bold), + ), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 8), + child: ElevatedButton.icon( + icon: Icon(Icons.support), + label: Text( + AppLocalizations.of(context)!.whichSupport), + onPressed: () async { + await Common.launchInBrowser( + 'https://docs.rsshub.app/joinus/#ti-jiao-xin-de-rsshub-gui-ze'); + })), + Padding( + padding: EdgeInsets.only(left: 24, right: 24), + child: ElevatedButton.icon( + icon: Icon(Icons.cloud_upload), + label: Text( + AppLocalizations.of(context)!.submitNewRules), + onPressed: () async { + await Common.launchInBrowser( + 'https://docs.rsshub.app/social-media.html#_755'); + })), + ], + ); + } + +} \ No newline at end of file diff --git a/lib/views/components/radar_card.dart b/lib/views/components/radar_card.dart new file mode 100644 index 0000000..e5b6d6f --- /dev/null +++ b/lib/views/components/radar_card.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:oktoast/oktoast.dart'; +import 'package:rssaid/common/link_helper.dart'; +import 'package:rssaid/models/radar.dart'; +import 'package:rssaid/shared_prefs.dart'; +import 'package:share/share.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class RadarCard extends StatelessWidget { + final Radar radar; + final SharedPrefs prefs; + + const RadarCard({Key? key, required this.radar, required this.prefs}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.only(left: 24, right: 24, bottom: 8, top: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15.0), + ), + elevation: 0, + child: Container( + margin: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(radar.title!, + style: TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.left), + SizedBox(height: 12,), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ElevatedButton.icon( + icon: Icon(Icons.copy), + label: Text(AppLocalizations.of(context)!.copy,), + onPressed: () async { + var url = await LinkHelper.getSubscriptionUrl(radar); + try { + Clipboard.setData(ClipboardData(text: url)); + } catch (e) { + showToastWidget( Text( + '${AppLocalizations.of(context)!.copyFailed}: ${e.toString()}', + )); + return; + } + await prefs.removeIfExist("currentParams"); + showToastWidget(Text( + AppLocalizations.of(context)!.copySuccess, + )); + }, + )), + Padding(padding: EdgeInsets.only(left: 6, right: 6)), + Expanded( + child: ElevatedButton.icon( + icon: Icon(Icons.shortcut), + label: Text(AppLocalizations.of(context)!.subscribe), + onPressed: () async { + var url = await LinkHelper.getSubscriptionUrl(radar); + Share.share('$url', subject: '${radar.title}'); + }, + )), + ], + ) + ], + ))); + } +} \ No newline at end of file diff --git a/lib/views/config.dart b/lib/views/config.dart index e502c5b..8e978f4 100644 --- a/lib/views/config.dart +++ b/lib/views/config.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:oktoast/oktoast.dart'; import 'package:rssaid/common/common.dart'; import 'package:rssaid/models/radar_config.dart'; import 'package:flutter/material.dart'; @@ -14,7 +15,6 @@ class ConfigDialog extends StatefulWidget { class _ConfigStateDialog extends State { final _formKey = new GlobalKey(); - final _scaffoldKey = new GlobalKey(); Future _prefs = SharedPreferences.getInstance(); TextEditingController _filterController = new TextEditingController(); TextEditingController _filterTitleController = new TextEditingController(); @@ -52,9 +52,7 @@ class _ConfigStateDialog extends State { @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldKey, appBar: AppBar( - backgroundColor: Colors.white, title: Text(AppLocalizations.of(context)!.gc), ), body: SingleChildScrollView( @@ -75,7 +73,7 @@ class _ConfigStateDialog extends State { Text( AppLocalizations.of(context)!.contentFilter, style: TextStyle( - fontSize: 12, color: Colors.grey[400]), + fontSize: 12), ), ListTile( leading: Text(AppLocalizations.of(context)!.caseSensitive), @@ -109,7 +107,7 @@ class _ConfigStateDialog extends State { ), Text(AppLocalizations.of(context)!.filtering, style: TextStyle( - fontSize: 12, color: Colors.grey[400])), + fontSize: 12)), TextFormField( autofocus: true, controller: _filterController, @@ -168,7 +166,7 @@ class _ConfigStateDialog extends State { child: Text( AppLocalizations.of(context)!.filterout, style: TextStyle( - fontSize: 12, color: Colors.grey[400]), + fontSize: 12), ), ), TextFormField( @@ -201,7 +199,7 @@ class _ConfigStateDialog extends State { child: Text( AppLocalizations.of(context)!.filter_others, style: TextStyle( - fontSize: 12, color: Colors.grey[400]), + fontSize: 12), )), TextFormField( autofocus: true, @@ -282,7 +280,6 @@ class _ConfigStateDialog extends State { onChanged: (value) { setState(() => _onlyOnce = value!); }, - activeColor: Colors.orange, ), Text(AppLocalizations.of(context)!.rule_only_once) ]), @@ -307,10 +304,7 @@ class _ConfigStateDialog extends State { final SharedPreferences prefs = await _prefs; prefs.clear(); - _scaffoldKey.currentState!.showSnackBar( - SnackBar( - behavior: SnackBarBehavior.floating, - content: Text(AppLocalizations.of(context)!.reset_config_hint))); + showToastWidget(Text(AppLocalizations.of(context)!.reset_config_hint)); }, label: Text( AppLocalizations.of(context)!.reset, @@ -325,8 +319,7 @@ class _ConfigStateDialog extends State { _saveConfig() async { if (_formKey.currentState!.validate()) { await _parseConfig(); - _scaffoldKey.currentState!.showSnackBar(SnackBar( - behavior: SnackBarBehavior.floating, content: Text(AppLocalizations.of(context)!.save_config_hint))); + showToastWidget(Text(AppLocalizations.of(context)!.save_config_hint)); } } diff --git a/lib/views/home.dart b/lib/views/home.dart index 1948caa..b72f94c 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -13,9 +13,10 @@ import 'package:rssaid/common/link_helper.dart'; import 'package:rssaid/models/radar.dart'; import 'package:rssaid/radar/radar.dart'; import 'package:rssaid/shared_prefs.dart'; +import 'package:rssaid/views/components/not_found.dart'; +import 'package:rssaid/views/components/radar_card.dart'; import 'package:rssaid/views/config.dart'; import 'package:rssaid/views/settings.dart'; -import 'package:share/share.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -31,11 +32,7 @@ class _HomePageState extends State { String _currentUrl = ""; Future>? _radarList; bool _configVisible = false; - late ScrollController _scrollViewController; - bool _showAppbar = true; - bool _isScrollingDown = false; bool _notUrlDetected = false; - var _scaffoldKey = new GlobalKey(); late StreamSubscription _intentDataStreamSubscription; TextEditingController _inputUrlController = new TextEditingController(); late HeadlessInAppWebView headlessWebView; @@ -46,42 +43,20 @@ class _HomePageState extends State { super.initState(); // _fetchRules(); // For sharing or opening urls/text coming from outside the app while the app is in the memory - _intentDataStreamSubscription = ReceiveSharingIntent.instance.getMediaStream() + _intentDataStreamSubscription = ReceiveSharingIntent.instance + .getMediaStream() .where((event) => event.isNotEmpty) .listen(_detectUrlFromShare, onError: (err) {}); // For sharing or opening urls/text coming from outside the app while the app is closed ReceiveSharingIntent.instance.getInitialMedia().then((value) => { - { - _detectUrlFromShare(value) - } - }); - - _scrollViewController = new ScrollController(); - _scrollViewController.addListener(() { - if (_scrollViewController.position.userScrollDirection == - ScrollDirection.reverse) { - if (!_isScrollingDown) { - _isScrollingDown = true; - _showAppbar = false; - setState(() {}); - } - } - - if (_scrollViewController.position.userScrollDirection == - ScrollDirection.forward) { - if (_isScrollingDown) { - _isScrollingDown = false; - _showAppbar = true; - setState(() {}); - } - } - }); + {_detectUrlFromShare(value)} + }); headlessWebView = new HeadlessInAppWebView( onConsoleMessage: (controller, consoleMessage) { - print("CONSOLE MESSAGE: " + consoleMessage.message); - }, onWebViewCreated: (controller) { + print("CONSOLE MESSAGE: " + consoleMessage.message); + }, onWebViewCreated: (controller) { webViewController = controller; }); } @@ -93,7 +68,7 @@ class _HomePageState extends State { headlessWebView.dispose(); } - Future _detectUrlFromShare(List mediaFiles) async { + Future _detectUrlFromShare(List mediaFiles) async { if (mediaFiles.isEmpty) { return; } @@ -104,15 +79,14 @@ class _HomePageState extends State { String text = mediaFiles.first.path; - setState(() { _currentUrl = ''; _configVisible = false; _notUrlDetected = false; }); var links = linkify(text.trim(), - options: LinkifyOptions(humanize: false), - linkifiers: [UrlLinkifier()]) + options: LinkifyOptions(humanize: false), + linkifiers: [UrlLinkifier()]) .where((element) => element is LinkableElement); if (links.isNotEmpty) { _radarList = _detectUrl(links.first.text); @@ -131,10 +105,7 @@ class _HomePageState extends State { } }); } else { - _scaffoldKey.currentState!.showSnackBar(SnackBar( - elevation: 0, - behavior: SnackBarBehavior.floating, - content: Text(AppLocalizations.of(context)!.notfoundinshare))); + showToastWidget(Text(AppLocalizations.of(context)!.notfoundinshare)); } } @@ -150,10 +121,10 @@ class _HomePageState extends State { if (link != null && link.isNotEmpty) { _callRadar(link); } else { - _showSnackBar(AppLocalizations.of(context)!.notfoundinClipboard); + showToast(AppLocalizations.of(context)!.notfoundinClipboard); } } else { - _showSnackBar(AppLocalizations.of(context)!.notfoundinClipboard); + showToast(AppLocalizations.of(context)!.notfoundinClipboard); } } @@ -162,7 +133,7 @@ class _HomePageState extends State { setState(() => _currentUrl = url); prefs.record = url; _radarList!.then( - (value) { + (value) { if (value.length > 0) { print("_radarList length > 0, set _configVisible is true"); setState(() { @@ -220,22 +191,16 @@ class _HomePageState extends State { return [...radarList, ...await RssPlus.detecting(url)]; } - @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldKey, - backgroundColor: Color.fromARGB(255, 255, 255, 255), - appBar: _showAppbar - ? AppBar( - backgroundColor: Colors.white, + appBar: AppBar( centerTitle: false, title: Text("RSSAid", style: Theme.of(context).textTheme.titleLarge), actions: [ IconButton( icon: Icon( Icons.settings, - color: Colors.orange, ), onPressed: () { Navigator.push( @@ -244,333 +209,193 @@ class _HomePageState extends State { builder: (context) => new SettingPage())); }) ], - ) - : null, + ), body: SafeArea( child: SingleChildScrollView( - controller: _scrollViewController, child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.start, - children: [ - _buildCustomLinkPreview(context), - Padding( - padding: - EdgeInsets.only(left: 24, right: 24, top: 8, bottom: 8), - child: ElevatedButton.icon( - icon: Icon(Icons.copy_outlined, color: Colors.orange), - label: Text(AppLocalizations.of(context)!.fromClipboard, - style: TextStyle(color: Colors.orange)), - onPressed: _detectUrlByClipboard)), - Padding( - padding: + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 0, bottom: 8), - child: ElevatedButton.icon( - icon: Icon(Icons.input, color: Colors.orange), - label: Text( - AppLocalizations.of(context)!.inputbyKeyboard, - style: TextStyle(color: Colors.orange)), - onPressed: _showInputDialog)), - _createRadarList(context), - if (_currentUrl == '') _historyList() - ], - )), + child: TextField( + decoration: InputDecoration( + hintText: AppLocalizations.of(context)!.inputLinkChecked, + ), + controller: _inputUrlController, + autofocus: true, + textInputAction: TextInputAction.done, + onEditingComplete: submitInputUrl, + )), + Padding( + padding: + EdgeInsets.only(left: 24, right: 24, top: 8, bottom: 8), + child: ElevatedButton.icon( + icon: Icon(Icons.copy_outlined), + label: Text(AppLocalizations.of(context)!.fromClipboard), + onPressed: _detectUrlByClipboard)), + _buildCustomLinkPreview(context), + _createRadarList(context), + if (_currentUrl == '') _historyList() + ], + )), ), floatingActionButton: _configVisible ? FloatingActionButton( - tooltip: AppLocalizations.of(context)!.addConfig, - child: Icon(Icons.post_add, color: Colors.white), - onPressed: () { - Navigator.of(context).push(new MaterialPageRoute( - builder: (BuildContext context) { - return new ConfigDialog(); + tooltip: AppLocalizations.of(context)!.addConfig, + child: Icon(Icons.post_add), + onPressed: () { + Navigator.of(context).push(new MaterialPageRoute( + builder: (BuildContext context) { + return new ConfigDialog(); + }, + fullscreenDialog: true)); }, - fullscreenDialog: true)); - }, - backgroundColor: Colors.orange, - ) + ) : null, ); } + Widget trailingWidget() { + if (!(_configVisible || _notUrlDetected)) { + return SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator(strokeWidth: 2)); + } + return IconButton( + padding: EdgeInsets.zero, + icon: Icon(Icons.close), + onPressed: () { + setState(() { + _currentUrl = ""; + _configVisible = false; + _notUrlDetected = false; + _radarList = null; + }); + }); + } + Widget _buildCustomLinkPreview(BuildContext context) { if (_currentUrl.trim().length != 0) { return Card( margin: EdgeInsets.only(left: 24, right: 24, top: 8, bottom: 8), - color: Color.fromARGB(255, 242, 242, 247), elevation: 0, child: Padding( padding: EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 8), child: Column( children: [ - SizedBox( - height: 30, - child: Row( - children: [ - Expanded( - child: Text(_currentUrl, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold)), - ), - if (!(_configVisible || _notUrlDetected)) - SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(strokeWidth: 2)) - else - IconButton( - padding: EdgeInsets.zero, - splashRadius: 20, - icon: Icon(Icons.close), - onPressed: () { - setState(() { - _currentUrl = ""; - _configVisible = false; - _notUrlDetected = false; - _radarList = null; - }); - }) - ], + Card( + child: ListTile( + dense: true, + title: Text(_currentUrl, + maxLines: 1, overflow: TextOverflow.ellipsis), + trailing: trailingWidget(), ), ), + SizedBox( + height: 8, + ), AnyLinkPreview( link: _currentUrl.trim(), displayDirection: UIDirection.uiDirectionHorizontal, cache: Duration(hours: 1), - backgroundColor: Colors.grey[300], errorWidget: Container( - color: Colors.grey[300], child: Text('Oops!'), ), ), ], ))); } - return Container(); + return SizedBox.shrink(); } Widget _createRadarList(BuildContext context) { return FutureBuilder( future: _radarList, builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data != null) { + if (snapshot.hasData && + snapshot.data != null && + (snapshot.data as List).length > 0) { List radarList = snapshot.data as List; return ListView.builder( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: radarList.length, - itemBuilder: (context, index) => _buildRssWidget(radarList[index]), + itemBuilder: (context, index) => RadarCard( + radar: radarList[index], + prefs: prefs, + ), ); } return _notUrlDetected - ? Column( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset('assets/imgs/404.png'), - Text( - AppLocalizations.of(context)!.notfound, - style: TextStyle(fontSize: 12), - ), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 8), - child: ElevatedButton.icon( - icon: Icon(Icons.support, color: Colors.orange), - label: Text( - AppLocalizations.of(context)!.whichSupport, - style: TextStyle(color: Colors.orange)), - onPressed: () async { - await Common.launchInBrowser( - 'https://docs.rsshub.app/joinus/#ti-jiao-xin-de-rsshub-gui-ze'); - })), - Padding( - padding: EdgeInsets.only(left: 24, right: 24), - child: ElevatedButton.icon( - icon: Icon(Icons.cloud_upload, color: Colors.orange), - label: Text( - AppLocalizations.of(context)!.submitNewRules, - style: TextStyle(color: Colors.orange)), - onPressed: () async { - await Common.launchInBrowser( - 'https://docs.rsshub.app/social-media.html#_755'); - })), - ], - ) + ? NotFound() : Container(); }, ); } - Widget _buildRssWidget(Radar radar) { - return Card( - margin: EdgeInsets.only(left: 24, right: 24, bottom: 8, top: 16), - color: Color.fromARGB(255, 242, 242, 247), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15.0), - ), - elevation: 0, - child: Container( - margin: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text(radar.title!, - style: TextStyle(fontWeight: FontWeight.bold), - textAlign: TextAlign.center), - Row( - children: [ - Expanded( - child: ElevatedButton.icon( - icon: Icon( - Icons.copy, - color: Colors.orange, - ), - label: Text( - AppLocalizations.of(context)!.copy, - style: TextStyle(color: Colors.orange), - ), - onPressed: () async { - var url = await LinkHelper.getSubscriptionUrl(radar); - - try { - Clipboard.setData(ClipboardData(text: url)); - } catch (e) { - _scaffoldKey.currentState!.showSnackBar(SnackBar( - behavior: SnackBarBehavior.floating, - content: Text( - '${AppLocalizations.of(context)!.copyFailed}: ${e.toString()}', - ))); - return; - } - await prefs.removeIfExist("currentParams"); - _scaffoldKey.currentState!.showSnackBar(SnackBar( - behavior: SnackBarBehavior.floating, - content: Text( - AppLocalizations.of(context)!.copySuccess, - ))); - }, - )), - Padding(padding: EdgeInsets.only(left: 6, right: 6)), - Expanded( - child: ElevatedButton.icon( - icon: Icon(Icons.done, color: Colors.orange), - label: Text(AppLocalizations.of(context)!.subscribe, - style: TextStyle(color: Colors.orange)), - onPressed: () async { - var url = await LinkHelper.getSubscriptionUrl(radar); - Share.share('$url', subject: '${radar.title}'); - }, - )), - ], - ) - ], - ))); - } - ///History recoeds list widget Widget _historyList() { var history = prefs.historyList; return Column( - children: [ - SizedBox(height: 20), - ListView.builder( - shrinkWrap: true, - itemCount: history.length, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return Dismissible( - key: ValueKey(history[index]), - onDismissed: (direction) { - setState(() => history.removeAt(index)); - prefs.historyList = history; - if (mounted) setState(() {}); - }, - child: Card( - margin: - EdgeInsets.only(left: 24, right: 24, bottom: 12), - color: Color.fromARGB(255, 242, 242, 247), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0)), - elevation: 0, - child: InkWell( - onTap: () => _callRadar(history[index]), - borderRadius: BorderRadius.circular(10.0), - child: Padding( - padding: EdgeInsets.all(16), - child: Text(history[index], - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.orange)), - ), - )), - ); - }, - ), - Padding( - padding: - EdgeInsets.only(left: 24, right: 24, top: 0, bottom: 8), - child: ElevatedButton.icon( - icon: Icon(Icons.clear_all_outlined, - color: Colors.orange), - label: Text(AppLocalizations.of(context)!.clear, - style: TextStyle(color: Colors.orange)), - onPressed: () { - prefs.historyList = []; - })), - ], + children: [ + SizedBox(height: 20), + ListView.builder( + shrinkWrap: true, + itemCount: history.length, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Dismissible( + key: ValueKey(history[index]), + onDismissed: (direction) { + setState(() => history.removeAt(index)); + prefs.historyList = history; + if (mounted) setState(() {}); + }, + child: Card( + margin: EdgeInsets.only(left: 24, right: 24, bottom: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0)), + elevation: 0, + child: InkWell( + onTap: () => _callRadar(history[index]), + borderRadius: BorderRadius.circular(10.0), + child: Padding( + padding: EdgeInsets.all(16), + child: Text(history[index], + maxLines: 1, overflow: TextOverflow.ellipsis), + ), + )), ); - - - } - - /// 显示输入地址框 - Future _showInputDialog() async { - return showDialog( - context: context, - barrierDismissible: true, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - AppLocalizations.of(context)!.inputLinkChecked, - ), - content: Container( - child: TextField( - controller: _inputUrlController, - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - var url = _inputUrlController.text.trim(); - if (url.length > 0) { - var link = LinkHelper.verifyLink(url); - if (link != null) { - _callRadar(link); - return; - } else { - _showSnackBar(AppLocalizations.of(context)!.linkError); - } - } else { - _showSnackBar(AppLocalizations.of(context)!.notfound); - } - }, - child: Text( - AppLocalizations.of(context)!.sure, - )) - ], - ); - }); + }, + ), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 0, bottom: 8), + child: ElevatedButton.icon( + icon: Icon(Icons.clear_all_outlined), + label: Text(AppLocalizations.of(context)!.clear), + onPressed: () { + prefs.historyList = []; + })), + ], + ); } - _showSnackBar(String text) { - _scaffoldKey.currentState!.showSnackBar(SnackBar( - elevation: 0, - behavior: SnackBarBehavior.floating, - content: Text(text))); + void submitInputUrl() { + var url = _inputUrlController.text.trim(); + if (url.length > 0) { + var link = LinkHelper.verifyLink(url); + if (link != null) { + _callRadar(link); + return; + } else { + showToast(AppLocalizations.of(context)!.linkError); + } + } else { + showToast(AppLocalizations.of(context)!.notfound); + } } -} \ No newline at end of file +} diff --git a/lib/views/rules.dart b/lib/views/rules.dart index a2a9d5d..47c7c15 100644 --- a/lib/views/rules.dart +++ b/lib/views/rules.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:oktoast/oktoast.dart'; import 'package:rssaid/common/common.dart'; class RulesDialog extends StatefulWidget { @@ -28,21 +29,20 @@ class _RulesDialog extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - backgroundColor: Colors.white, centerTitle: true, title: Text( "Rules", style: Theme.of(context).textTheme.titleMedium, ), leading: IconButton( - icon: Icon(Icons.arrow_back_ios, color: Colors.black), + icon: Icon(Icons.arrow_back_ios), onPressed: () { Navigator.pop(context); }, ), actions: [ IconButton( - icon: Icon(Icons.settings, color: Colors.black), + icon: Icon(Icons.settings), onPressed: () { _showDialog(context); }, @@ -52,14 +52,13 @@ class _RulesDialog extends State { body: SingleChildScrollView(child: Container(child: Text(_rules))), floatingActionButton: FloatingActionButton( tooltip: AppLocalizations.of(context)!.refreshRules, - child: Icon(Icons.refresh, color: Colors.white), + child: Icon(Icons.refresh), onPressed: () { _refreshRules(context); Future.delayed(Duration(seconds: 3), () { Navigator.pop(context); }); }, - backgroundColor: Colors.orange, )); } @@ -99,25 +98,18 @@ class _RulesDialog extends State { ), actions: [ new TextButton( - child: Text(AppLocalizations.of(context)!.cancel, - style: TextStyle(color: Colors.orange)), + child: Text(AppLocalizations.of(context)!.cancel), onPressed: () { Navigator.pop(context); }), new TextButton( - child: Text(AppLocalizations.of(context)!.sure, - style: TextStyle(color: Colors.orange)), + child: Text(AppLocalizations.of(context)!.sure), onPressed: () async { if (_ruleSourcecontroller.text.isNotEmpty) { bool result = await Common.setRuleSource( _ruleSourcecontroller.text.trim()); if (result) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(AppLocalizations.of(context)! - .save_config_hint), - elevation: 0, - behavior: SnackBarBehavior.floating, - )); + showToastWidget(Text(AppLocalizations.of(context)!.save_config_hint),); } } Navigator.pop(context); diff --git a/lib/views/settings.dart b/lib/views/settings.dart index bbab71b..8f2ba6c 100644 --- a/lib/views/settings.dart +++ b/lib/views/settings.dart @@ -53,12 +53,11 @@ class _SettingPageState extends State { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( - backgroundColor: Colors.white, centerTitle: true, title: Text(AppLocalizations.of(context)!.settings, style: Theme.of(context).textTheme.titleLarge), leading: IconButton( - icon: Icon(Icons.arrow_back_ios, color: Colors.black), + icon: Icon(Icons.arrow_back_ios), onPressed: () { Navigator.pop(context); })), @@ -117,10 +116,9 @@ class CommonRows extends StatelessWidget { ), elevation: 1, child: ListTile( - leading: Icon(Icons.domain, color: Colors.orange), + leading: Icon(Icons.domain), title: Text( "RSSHub URL", - style: TextStyle(color: Colors.orange), ), onTap: () { _showDialog(context); @@ -138,10 +136,9 @@ class CommonRows extends StatelessWidget { ), elevation: 1, child: ListTile( - leading: Icon(Icons.apps, color: Colors.orange), + leading: Icon(Icons.apps), title: Text( AppLocalizations.of(context)!.user_manual, - style: TextStyle(color: Colors.orange), ), onTap: () { Common.launchInBrowser("https://docs.rsshub.app"); @@ -159,10 +156,9 @@ class CommonRows extends StatelessWidget { ), elevation: 1, child: ListTile( - leading: Icon(Icons.note, color: Colors.orange), + leading: Icon(Icons.note), title: Text( "Rules", - style: TextStyle(color: Colors.orange), ), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (_) { @@ -189,14 +185,12 @@ class CommonRows extends StatelessWidget { ), actions: [ TextButton( - child: Text(AppLocalizations.of(context)!.cancel, - style: TextStyle(color: Colors.orange)), + child: Text(AppLocalizations.of(context)!.cancel), onPressed: () { Navigator.pop(context); }), ElevatedButton( - child: Text(AppLocalizations.of(context)!.sure, - style: TextStyle(color: Colors.orange)), + child: Text(AppLocalizations.of(context)!.sure), onPressed: () { if (_domainController.text != _domain) { _domainSetCallback!(_domainController.text); @@ -226,10 +220,9 @@ class AboutRows extends StatelessWidget { ), elevation: 1, child: ListTile( - leading: Icon(icon, color: Colors.orange), + leading: Icon(icon), title: Text( name, - style: TextStyle(color: Colors.orange), ), trailing: trailing.isNotEmpty ? Text(trailing) : null, onTap: url.isNotEmpty