import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform; import 'package:flutter/material.dart'; import 'package:signals/signals_flutter.dart' show SignalBuilder; import '../../application/pages/mews_urls.dart'; import '../app.dart'; /// The single-field address bar. The left icon opens the hub and, on pointer /// platforms, carries the back and forward buttons; the right icons reload, /// subscribe (only when the page offers something to subscribe to) and /// bookmark the current page. class AddressBar extends StatefulWidget { const AddressBar({super.key}); @override State createState() => _AddressBarState(); } class _AddressBarState extends State { final _controller = TextEditingController(); final _focus = FocusNode(); bool _focused = false; @override void initState() { super.initState(); _focus.addListener(() { if (_focus.hasFocus != _focused) { setState(() => _focused = _focus.hasFocus); if (_focused) { _controller.selection = TextSelection(baseOffset: 0, extentOffset: _controller.text.length); } } }); } @override void dispose() { _focus.dispose(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final deps = AppScope.of(context).deps; final strings = AppScope.of(context).strings; return SignalBuilder(builder: (context) { final page = deps.browser.currentPage.value; final url = page?.url; if (url != null && !_focused) { _controller.text = url; } // Touch devices navigate with swipes; desktops get buttons. final hasButtons = switch (defaultTargetPlatform) { TargetPlatform.linux || TargetPlatform.windows || TargetPlatform.macOS => true, _ => false, }; // Only pages with dated entry links or an Atom feed can be subscribed // to (mews spec, section 6). final canSubscribe = page != null && !MewsUrls.isInternal(page.url) && (page.atomFeedHref != null || page.entries.isNotEmpty); final canBookmark = page != null && !MewsUrls.isInternal(page.url); // One button, two layouts: on pointer platforms it sits beside the // back and forward buttons. final hubButton = IconButton( icon: const Icon(Icons.menu), tooltip: strings.t('reader-open-hub'), onPressed: () => deps.browser.open(MewsUrls.hub), ); return Padding( padding: const EdgeInsets.fromLTRB(8, 8, 8, 4), child: TextField( controller: _controller, focusNode: _focus, autofocus: false, keyboardType: TextInputType.url, textInputAction: TextInputAction.go, autocorrect: false, enableSuggestions: false, onSubmitted: (value) { _focus.unfocus(); deps.browser.open(value); }, decoration: InputDecoration( hintText: strings.t('reader-address-placeholder'), labelText: strings.t('reader-address-label'), isDense: true, border: const OutlineInputBorder(), prefixIcon: hasButtons ? Row( mainAxisSize: MainAxisSize.min, children: [ hubButton, IconButton( icon: const Icon(Icons.arrow_back), tooltip: strings.t('reader-back'), onPressed: deps.browser.canGoBack ? deps.browser.back : null, ), IconButton( icon: const Icon(Icons.arrow_forward), tooltip: strings.t('reader-forward'), onPressed: deps.browser.canGoForward ? deps.browser.forward : null, ), ], ) : hubButton, prefixIconConstraints: hasButtons ? const BoxConstraints(minWidth: 138, minHeight: 48) : null, suffixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.refresh), tooltip: strings.t('reader-reload'), onPressed: () => deps.browser.reload(), ), if (canSubscribe) IconButton( icon: Icon( deps.library.isSubscribed(page.url) ? Icons.notifications_active : Icons.notifications_outlined, ), tooltip: deps.library.isSubscribed(page.url) ? strings.t('reader-unsubscribe') : strings.t('reader-subscribe'), onPressed: () => deps.library .toggleSubscription(page.url, page.title, page.entries), ), if (canBookmark) IconButton( icon: Icon( deps.library.isBookmarked(page.url) ? Icons.star : Icons.star_border, ), tooltip: deps.library.isBookmarked(page.url) ? strings.t('reader-bookmark-remove') : strings.t('reader-bookmark-add'), onPressed: () => deps.library.toggleBookmark(page.url, page.title), ), ], ), ), ), ); }); } }