Skip to content

Instantly share code, notes, and snippets.

@Ahmadre
Last active July 5, 2022 10:59
Show Gist options
  • Save Ahmadre/2b124875ed5adbd43de6d73b3d25f3cf to your computer and use it in GitHub Desktop.
Save Ahmadre/2b124875ed5adbd43de6d73b3d25f3cf to your computer and use it in GitHub Desktop.
Generic Types in Flutter
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
/// Main
void main() {
runApp(GenericTypesApp());
}
/// EOF Main
/// Models
class Product {
final String title;
final String? description;
final double price;
final String? size;
final Color? color;
Product({
required this.title,
this.description,
required this.price,
this.size,
this.color,
});
Product copyWith({
final String? title,
final String? description,
final double? price,
final String? size,
final Color? color,
}) {
return Product(
title: title ?? this.title,
description: description ?? this.description,
price: price ?? this.price,
size: size ?? this.size,
color: color ?? this.color,
);
}
@override
String toString() {
return 'Product(title: $title, description: $description, price: $price, size: $size, color: $color)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Product &&
other.title == title &&
other.description == description &&
other.price == price &&
other.size == size &&
other.color == color;
}
@override
int get hashCode {
return title.hashCode ^
description.hashCode ^
price.hashCode ^
size.hashCode ^
color.hashCode;
}
}
enum Size {
xs('XS'),
s('S'),
m('M'),
l('L'),
xl('XL'),
xxl('XXL');
final String value;
const Size(this.value);
}
/// EOF Models
/// App
class GenericTypesApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const ProductPage(),
);
}
}
/// EOF App
class ProductPage extends StatefulWidget {
const ProductPage({
Key? key,
}) : super(key: key);
@override
ProductPageState createState() => ProductPageState();
}
class ProductPageState extends State<ProductPage> {
Product product = Product(
title: 'Dash Holiday Shirt',
price: 20,
color: Colors.black,
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Shirt(product: product),
const SizedBox(
height: 25,
),
Text(
product.title,
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(
height: 15,
),
Text(
'${product.price.toString()} €',
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(
height: 25,
),
SingleOptionsPicker<String>(
options: {
Size.xs.value: 1,
Size.s.value: 3,
Size.m.value: 5,
Size.l.value: 10,
Size.xl.value: 7,
Size.xxl.value: 15,
},
onSingleOptionSet: (String option) => setState(() {
product = product.copyWith(size: option);
}),
),
const SizedBox(
height: 15,
),
SingleOptionsPicker<Color>(
options: {
Colors.black: 3,
Colors.blue: 1,
Colors.white: 4,
Colors.red: 10,
Colors.yellow: 5,
Colors.green: 3,
},
onSingleOptionSet: (Color option) => setState(() {
product = product.copyWith(color: option);
}),
),
],
),
),
);
}
}
class SingleOptionsPicker<T> extends StatelessWidget {
const SingleOptionsPicker({
Key? key,
required this.options,
required this.onSingleOptionSet,
}) : super(key: key);
final Map<T, int> options;
final ValueChanged<T> onSingleOptionSet;
@override
Widget build(BuildContext context) {
return OptionsPicker<T>(
options: options,
onOptionSet: (Set<T> options) => onSingleOptionSet(options.first),
allowMultiple: false,
);
}
}
class OptionsPicker<T> extends StatefulWidget {
const OptionsPicker({
Key? key,
required this.options,
required this.onOptionSet,
this.allowMultiple = false,
}) : super(key: key);
final Map<T, int> options;
final ValueChanged<Set<T>> onOptionSet;
final bool allowMultiple;
@override
OptionsPickerState<T> createState() => OptionsPickerState<T>();
}
class OptionsPickerState<T> extends State<OptionsPicker<T>> {
late Set<int> selectedOptions;
late List<T> optionsList;
@override
void initState() {
super.initState();
selectedOptions = <int>{0};
optionsList = widget.options.keys.toList();
}
@override
Widget build(BuildContext context) {
final toggleContent = <Widget>[];
widget.options.forEach(
(T key, int quantity) {
toggleContent.add(
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
child: key is! Color
? Text(
(key as String).toUpperCase(),
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
),
)
: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: key,
shape: BoxShape.circle,
),
),
),
);
},
);
final List<bool> selectedIndices = List.generate(
optionsList.length,
(int index) => selectedOptions.contains(index),
);
return ToggleButtons(
isSelected: selectedIndices,
renderBorder: T is! Color,
borderColor: T is Color ? Colors.transparent : null,
borderRadius: BorderRadius.circular(15),
children: toggleContent,
onPressed: (int index) {
if (!widget.allowMultiple) {
if (selectedOptions.contains(index)) {
return;
}
selectedOptions
..clear()
..add(index);
} else {
if (selectedOptions.contains(index)) {
if (selectedOptions.length > 1) {
selectedOptions.remove(index);
}
} else {
selectedOptions.add(index);
}
}
final Set<T> selectedSet = {};
for (int i in selectedOptions) {
selectedSet.add(optionsList[i]);
}
widget.onOptionSet(selectedSet);
},
);
}
}
class Shirt extends StatelessWidget {
const Shirt({
Key? key,
required this.product,
}) : super(key: key);
final Product product;
final String shirtSource =
'https://firebasestorage.googleapis.com/v0/b/flutter-course-app-logik.appspot.com/o/shirt.png?alt=media&token=c909366a-6914-4e33-928b-6180bb5e842f';
final String dashSource =
'https://firebasestorage.googleapis.com/v0/b/flutter-course-app-logik.appspot.com/o/dash.png?alt=media&token=07ef5f44-6162-47f0-a86b-78dea6e3c75c';
final double colorIntensity = .7;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Image.network(
shirtSource,
color: product.color?.withOpacity(colorIntensity),
colorBlendMode: BlendMode.srcATop,
),
Center(
child: Transform.translate(
offset: const Offset(0, -80),
child: Opacity(
opacity: .9,
child: Image.network(
dashSource,
width: 250,
height: 250,
),
),
),
),
],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment