316 lines
11 KiB
Dart
316 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:search_engine/screens/config.dart';
|
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'
|
|
as nav;
|
|
|
|
class BaseScreen extends StatelessWidget {
|
|
final Widget child;
|
|
final String? title;
|
|
final bool showSearchBar;
|
|
final bool showSettingsButton;
|
|
final bool showUserAvatar;
|
|
final TextEditingController? searchController;
|
|
final Function(String)? onSearchChanged;
|
|
final Function(String)? onSearchSubmitted;
|
|
final String? searchHintText;
|
|
final Widget? searchSuffixIcon;
|
|
final EdgeInsetsGeometry contentPadding;
|
|
final Widget? drawer;
|
|
final bool returnButton;
|
|
|
|
const BaseScreen({
|
|
super.key,
|
|
required this.child,
|
|
this.title,
|
|
this.showSearchBar = false,
|
|
this.showSettingsButton = false,
|
|
this.showUserAvatar = false,
|
|
this.searchController,
|
|
this.onSearchChanged,
|
|
this.onSearchSubmitted,
|
|
this.searchHintText,
|
|
this.searchSuffixIcon,
|
|
this.contentPadding = const EdgeInsets.all(20.0),
|
|
this.drawer,
|
|
this.returnButton = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isLandscape =
|
|
MediaQuery.of(context).orientation == Orientation.landscape;
|
|
|
|
return Scaffold(
|
|
drawer: drawer,
|
|
drawerEnableOpenDragGesture: true,
|
|
drawerEdgeDragWidth: 50,
|
|
onDrawerChanged: (isOpened) {},
|
|
body: SafeArea(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Layout adaptativo según la orientación
|
|
if (isLandscape)
|
|
_buildLandscapeHeader(context)
|
|
else
|
|
_buildPortraitHeader(context),
|
|
|
|
// Content area
|
|
Expanded(
|
|
child: child,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Layout para orientación horizontal (landscape)
|
|
Widget _buildLandscapeHeader(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
|
|
child: Row(
|
|
children: [
|
|
// Navigation icon (back button or drawer)
|
|
if (returnButton)
|
|
IconButton(
|
|
icon: const Icon(Icons.arrow_back, size: 20),
|
|
onPressed: () => Navigator.pop(context),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
visualDensity: VisualDensity.compact,
|
|
)
|
|
else if (drawer != null)
|
|
IconButton(
|
|
icon: const Icon(Icons.menu, size: 20),
|
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
visualDensity: VisualDensity.compact,
|
|
),
|
|
|
|
// Title
|
|
if (title != null)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
child: Text(
|
|
title!,
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
// Search bar inline
|
|
if (showSearchBar && searchController != null)
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 34,
|
|
child: TextFormField(
|
|
decoration: InputDecoration(
|
|
fillColor: const Color(0xFFEBEBEB),
|
|
filled: true,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
prefixIcon:
|
|
const Icon(Icons.search, color: Colors.grey, size: 18),
|
|
hintText: searchHintText ?? 'search_placeholder'.tr(),
|
|
hintStyle: const TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 14,
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 4),
|
|
isDense: true,
|
|
suffixIcon: searchController?.text.isNotEmpty ?? false
|
|
? IconButton(
|
|
icon: const Icon(Icons.clear, size: 18),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
onPressed: () {
|
|
searchController?.clear();
|
|
onSearchChanged?.call('');
|
|
},
|
|
)
|
|
: null,
|
|
),
|
|
controller: searchController,
|
|
onChanged: onSearchChanged,
|
|
onFieldSubmitted: onSearchSubmitted,
|
|
autofocus: false,
|
|
autocorrect: true,
|
|
style: const TextStyle(fontSize: 14),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Settings button
|
|
if (showSettingsButton)
|
|
IconButton(
|
|
onPressed: () =>
|
|
nav.pushScreenWithoutNavBar(context, const ConfigView()),
|
|
icon: const Icon(Icons.settings, size: 20),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
visualDensity: VisualDensity.compact,
|
|
),
|
|
|
|
// User avatar
|
|
if (showUserAvatar)
|
|
const Padding(
|
|
padding: EdgeInsets.only(left: 4),
|
|
child: CircleAvatar(
|
|
backgroundColor: Colors.grey,
|
|
radius: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// Layout para orientación vertical (portrait)
|
|
Widget _buildPortraitHeader(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
// Header row with title and buttons
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 12.0,
|
|
right: 12.0,
|
|
top: 6.0,
|
|
bottom: 4.0,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// Navigation icon (back button or drawer)
|
|
if (returnButton)
|
|
IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () => Navigator.pop(context),
|
|
padding: const EdgeInsets.all(4),
|
|
constraints: const BoxConstraints(),
|
|
iconSize: 20,
|
|
)
|
|
else if (drawer != null)
|
|
IconButton(
|
|
icon: const Icon(Icons.menu),
|
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
|
padding: const EdgeInsets.all(4),
|
|
constraints: const BoxConstraints(),
|
|
iconSize: 20,
|
|
),
|
|
|
|
// Title
|
|
if (title != null)
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
child: Text(
|
|
title!,
|
|
style: const TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Settings button
|
|
if (showSettingsButton)
|
|
IconButton(
|
|
onPressed: () =>
|
|
nav.pushScreenWithoutNavBar(context, const ConfigView()),
|
|
icon: const Icon(Icons.settings, size: 20),
|
|
padding: const EdgeInsets.all(4),
|
|
constraints: const BoxConstraints(),
|
|
visualDensity: VisualDensity.compact,
|
|
),
|
|
|
|
// User avatar
|
|
if (showUserAvatar)
|
|
const Padding(
|
|
padding: EdgeInsets.only(left: 4),
|
|
child: CircleAvatar(
|
|
backgroundColor: Colors.grey,
|
|
radius: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Search bar in a separate row
|
|
if (showSearchBar && searchController != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
left: 12.0,
|
|
right: 12.0,
|
|
bottom: 6.0,
|
|
),
|
|
child: SizedBox(
|
|
height: 34,
|
|
child: TextFormField(
|
|
decoration: InputDecoration(
|
|
fillColor: const Color(0xFFEBEBEB),
|
|
filled: true,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
prefixIcon:
|
|
const Icon(Icons.search, color: Colors.grey, size: 18),
|
|
hintText: searchHintText ?? 'search_placeholder'.tr(),
|
|
hintStyle: const TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 14,
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 4),
|
|
isDense: true,
|
|
suffixIcon: searchController?.text.isNotEmpty ?? false
|
|
? IconButton(
|
|
icon: const Icon(Icons.clear, size: 18),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
onPressed: () {
|
|
searchController?.clear();
|
|
onSearchChanged?.call('');
|
|
},
|
|
)
|
|
: null,
|
|
),
|
|
controller: searchController,
|
|
onChanged: onSearchChanged,
|
|
onFieldSubmitted: onSearchSubmitted,
|
|
autofocus: false,
|
|
autocorrect: true,
|
|
style: const TextStyle(fontSize: 14),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|