Various Widgets - wurzelsand/flutter-memos GitHub Wiki
If a widget wants to be built differently, depending on the space available to it, a LayoutBuilder
could be used. For example, a widget wants to display image and text if there is enough space. And if the space is not enough, it wants to display only the image instead.
Here I'm using a LayoutBuilder
that shows the maximum space available to it as a red rectangle. A green rectangle above it represents how big it should be at least.
The red rectangle is 300x400, the green one 100x200.
import 'package:flutter/material.dart';
void main() {
runApp(const TestApp());
}
class TestApp extends StatelessWidget {
const TestApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Container(
constraints: const BoxConstraints(
minWidth: 100, minHeight: 200, maxWidth: 300, maxHeight: 400),
child: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: [
Container(
color: Colors.red,
width: constraints.maxWidth,
height: constraints.maxHeight),
Container(
color: Colors.green,
width: constraints.minWidth,
height: constraints.minHeight),
],
);
}),
),
),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'untitled',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late final AnimationController _controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
late final Animation<Offset> _animation =
Tween<Offset>(begin: Offset.zero, end: const Offset(1, 0)).animate( // #1
CurvedAnimation(parent: _controller, curve: Curves.easeOutQuint));
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SlideTransition(
position: _animation,
child: InkWell(
onTap: () async {
try {
await _controller.forward().orCancel;
} on TickerCanceled {
// the animation got canceled, probably because it was disposed of
}
_controller.reset();
},
child: Container(
width: 100,
height: 100,
margin: const EdgeInsets.all(50),
color: Colors.red,
),
),
),
);
}
}
-
Offset(1, 0)
means: 1 unit to the right, where the size of a unit depends on the size ofSlideTransitions
child: In this case 200 pixels (Containers
width + 2 *margin
).
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyCustomPainter(),
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
}
class MyCustomPainter extends CustomPainter {
double _radius = 0;
@override
void paint(Canvas canvas, Size size) {
_radius = size.shortestSide / 2;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), _radius,
Paint()..color = Colors.green);
}
@override
bool shouldRepaint(covariant MyCustomPainter oldDelegate) {
return false; // #1
}
}
- Usually we should compare
this
witholdDelegate
. But in this case, it does not seem necessary:paint
is always called anyway when the window size changes, withoutshouldRepaint
being called at all.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatelessWidget {
MyWidget({super.key});
final Future<String> hello =
Future.delayed(const Duration(seconds: 4), () => 'Hello');
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: hello,
builder: ((context, snapshot) {
final text = snapshot.connectionState == ConnectionState.done
? snapshot.data ?? ''
: 'waiting';
return Text(text, style: const TextStyle(fontSize: 64));
}),
);
}
}
flutter.dev/BottomNavigationBar
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatelesslWidget(),
);
}
}
class MyStatelesslWidget extends StatelessWidget {
const MyStatelesslWidget({super.key});
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('TabBar Sample'),
bottom: const TabBar(tabs: [
Tab(icon: Icon(Icons.home), text: 'Home'),
Tab(icon: Icon(Icons.business), text: 'Business'),
Tab(icon: Icon(Icons.school), text: 'School'),
]),
),
body: TabBarView(children: [
for (int i = 0; i < 3; i++)
Center(
child: _widgetOptions[i],
)
]),
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('NavigationRail Sample'),
),
body: Row(
children: [
NavigationRail(
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.business),
label: Text('Business'),
),
NavigationRailDestination(
icon: Icon(Icons.school),
label: Text('School'),
),
],
selectedIndex: _selectedIndex,
labelType: NavigationRailLabelType.all,
onDestinationSelected: _onItemTapped,
),
Expanded(
child: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index, BuildContext context) {
setState(() {
_selectedIndex = index;
});
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drawer Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
drawer: Drawer(
child: ListView(
children: [
ListTile(
title: const Text('Home'),
leading: const Icon(Icons.home),
onTap: () => _onItemTapped(0, context),
),
ListTile(
title: const Text('Business'),
leading: const Icon(Icons.business),
onTap: () => _onItemTapped(1, context),
),
ListTile(
title: const Text('School'),
leading: const Icon(Icons.school),
onTap: () => _onItemTapped(2, context),
),
],
),
),
);
}
}
flutter.dev/cookbook/validation
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyCustomForm(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 100,
child: TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
icon: Icon(Icons.text_fields),
),
// The validator receives the text that the user has entered.
validator: validateNotEmpty,
),
),
SizedBox(
height: 100,
child: TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
icon: Icon(Icons.numbers),
),
validator: validateNotEmpty
.then(validateIsNumber)
.then(validateIsInteger),
),
),
ElevatedButton(
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState!.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
},
child: const Text('Submit'),
),
],
),
),
);
}
String? validateNotEmpty(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
}
String? validateIsNumber(String? value) {
if (value == null) {
return '';
}
if (double.tryParse(value) == null) {
return 'Please enter a number';
}
return null;
}
String? validateIsInteger(String? value) {
if (value == null) {
return '';
}
if (int.tryParse(value) == null) {
return 'Please enter a valid integer number';
}
return null;
}
}
typedef ValidatorFunction = String? Function(String?);
extension Concatenation on ValidatorFunction {
ValidatorFunction then(ValidatorFunction validator) {
return (value) {
final messageText = this(value);
if (messageText == null) {
return validator(value);
}
return messageText;
};
}
}
-
I wanted to chain the validators:
validator: validateNotEmpty .then(validateIsNumber) .then(validateIsInteger),
therefore:
typedef ValidatorFunction = String? Function(String?); extension Concatenation on ValidatorFunction { ValidatorFunction then(ValidatorFunction validator) { return (value) { final messageText = this(value); if (messageText == null) { return validator(value); } return messageText; }; } }
A scrollable table should consist of 30 rows with 2 columns each. The columns are to be headed with Name
and Age
. The Name
column shall occupy 80 percent of the row width. The rows shall consist of input fields.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: SizedBox(
child: Table(
columnWidths: const {
0: FractionColumnWidth(0.8),
1: FractionColumnWidth(0.2)
},
border: TableBorder.all(),
children: [
const TableRow(
decoration: BoxDecoration(color: Colors.lightBlue),
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Name:',
style: TextStyle(fontSize: 16),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Age:',
style: TextStyle(fontSize: 16),
),
),
],
),
...List<TableRow>.generate(
30,
(index) {
return const TableRow(
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
decoration: null,
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
decoration: null,
),
),
],
);
},
)
],
),
),
),
),
),
);
}
}