md-app/lib/widgets/base.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),
),
),
),
],
);
}
}