Skip to content

Instantly share code, notes, and snippets.

@Klerith
Created March 22, 2023 19:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Klerith/0b7282fc4d3d83e686b8e47b5292b696 to your computer and use it in GitHub Desktop.
Save Klerith/0b7282fc4d3d83e686b8e47b5292b696 to your computer and use it in GitHub Desktop.
Pantalla de Producto y Custom Product Field
import 'package:flutter/material.dart';
class CustomProductField extends StatelessWidget {
final bool isTopField; // La idea es que tenga bordes redondeados arriba
final bool isBottomField; // La idea es que tenga bordes redondeados abajo
final String? label;
final String? hint;
final String? errorMessage;
final bool obscureText;
final TextInputType? keyboardType;
final int maxLines;
final String initialValue;
final Function(String)? onChanged;
final Function(String)? onFieldSubmitted;
final String? Function(String?)? validator;
const CustomProductField({
super.key,
this.isTopField = false,
this.isBottomField = false,
this.label,
this.hint,
this.errorMessage,
this.obscureText = false,
this.keyboardType = TextInputType.text,
this.maxLines = 1,
this.initialValue = '',
this.onChanged,
this.onFieldSubmitted,
this.validator,
});
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final border = OutlineInputBorder(
borderSide: const BorderSide(color: Colors.transparent),
borderRadius: BorderRadius.circular(40)
);
const borderRadius = Radius.circular(15);
return Container(
// padding: const EdgeInsets.only(bottom: 0, top: 15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: isTopField ? borderRadius : Radius.zero,
topRight: isTopField ? borderRadius : Radius.zero,
bottomLeft: isBottomField ? borderRadius : Radius.zero,
bottomRight: isBottomField ? borderRadius : Radius.zero,
),
boxShadow: [
if (isBottomField)
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 5,
offset: const Offset(0,3)
)
]
),
child: TextFormField(
onChanged: onChanged,
onFieldSubmitted: onFieldSubmitted,
validator: validator,
obscureText: obscureText,
keyboardType: keyboardType,
style: const TextStyle( fontSize: 15, color: Colors.black54 ),
maxLines: maxLines,
initialValue: initialValue,
decoration: InputDecoration(
floatingLabelBehavior: maxLines > 1 ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto,
floatingLabelStyle: const TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 15),
enabledBorder: border,
focusedBorder: border,
errorBorder: border.copyWith( borderSide: const BorderSide( color: Colors.transparent )),
focusedErrorBorder: border.copyWith( borderSide: const BorderSide( color: Colors.transparent )),
isDense: true,
label: label != null ? Text(label!) : null,
hintText: hint,
errorText: errorMessage,
focusColor: colors.primary,
// icon: Icon( Icons.supervised_user_circle_outlined, color: colors.primary, )
),
),
);
}
}
class _ProductView extends StatelessWidget {
final Product product;
const _ProductView({required this.product});
@override
Widget build(BuildContext context) {
final textStyles = Theme.of(context).textTheme;
return ListView(
children: [
SizedBox(
height: 250,
width: 600,
child: _ImageGallery(images: product.images ),
),
const SizedBox( height: 10 ),
Center(child: Text( product.title, style: textStyles.titleSmall )),
const SizedBox( height: 10 ),
_ProductInformation( product: product ),
],
);
}
}
class _ProductInformation extends ConsumerWidget {
final Product product;
const _ProductInformation({required this.product});
@override
Widget build(BuildContext context, WidgetRef ref ) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Generales'),
const SizedBox(height: 15 ),
CustomProductField(
isTopField: true,
label: 'Nombre',
initialValue: product.title,
),
CustomProductField(
isTopField: true,
label: 'Slug',
initialValue: product.slug,
),
CustomProductField(
isBottomField: true,
label: 'Precio',
keyboardType: const TextInputType.numberWithOptions(decimal: true),
initialValue: product.price.toString(),
),
const SizedBox(height: 15 ),
const Text('Extras'),
_SizeSelector(selectedSizes: product.sizes ),
const SizedBox(height: 5 ),
_GenderSelector( selectedGender: product.gender ),
const SizedBox(height: 15 ),
CustomProductField(
isTopField: true,
label: 'Existencias',
keyboardType: const TextInputType.numberWithOptions(decimal: true),
initialValue: product.stock.toString(),
),
CustomProductField(
maxLines: 6,
label: 'Descripción',
keyboardType: TextInputType.multiline,
initialValue: product.description,
),
CustomProductField(
isBottomField: true,
maxLines: 2,
label: 'Tags (Separados por coma)',
keyboardType: TextInputType.multiline,
initialValue: product.tags.join(', '),
),
const SizedBox(height: 100 ),
],
),
);
}
}
class _SizeSelector extends StatelessWidget {
final List<String> selectedSizes;
final List<String> sizes = const['XS','S','M','L','XL','XXL','XXXL'];
const _SizeSelector({required this.selectedSizes});
@override
Widget build(BuildContext context) {
return SegmentedButton(
showSelectedIcon: false,
segments: sizes.map((size) {
return ButtonSegment(
value: size,
label: Text(size, style: const TextStyle(fontSize: 10))
);
}).toList(),
selected: Set.from( selectedSizes ),
onSelectionChanged: (newSelection) {
print(newSelection);
},
multiSelectionEnabled: true,
);
}
}
class _GenderSelector extends StatelessWidget {
final String selectedGender;
final List<String> genders = const['men','women','kid'];
final List<IconData> genderIcons = const[
Icons.man,
Icons.woman,
Icons.boy,
];
const _GenderSelector({required this.selectedGender});
@override
Widget build(BuildContext context) {
return Center(
child: SegmentedButton(
multiSelectionEnabled: false,
showSelectedIcon: false,
style: const ButtonStyle(visualDensity: VisualDensity.compact ),
segments: genders.map((size) {
return ButtonSegment(
icon: Icon( genderIcons[ genders.indexOf(size) ] ),
value: size,
label: Text(size, style: const TextStyle(fontSize: 12))
);
}).toList(),
selected: { selectedGender },
onSelectionChanged: (newSelection) {
print(newSelection);
},
),
);
}
}
class _ImageGallery extends StatelessWidget {
final List<String> images;
const _ImageGallery({required this.images});
@override
Widget build(BuildContext context) {
return PageView(
scrollDirection: Axis.horizontal,
controller: PageController(
viewportFraction: 0.7
),
children: images.isEmpty
? [ ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: Image.asset('assets/images/no-image.jpg', fit: BoxFit.cover ))
]
: images.map((e){
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
child: Image.network(e, fit: BoxFit.cover,),
);
}).toList(),
);
}
}
@Miguel-A-Jara
Copy link

Gracias profe por el código!

@carlosdiazz
Copy link

EL Mejorrrr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment