blob: 26e9eed0fb3e22513a02b98df7f780674f440ef8 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for [ListenableBuilder].
import 'package:flutter/material.dart';
void main() => runApp(const ListenableBuilderExample());
/// This widget listens for changes in the focus state of the subtree defined by
/// its [child] widget, changing the border and color of the container it is in
/// when it has focus.
///
/// A [FocusListenerContainer] swaps out the [BorderSide] of a border around the
/// child widget with [focusedSide], and the background color with
/// [focusedColor], when a widget that is a descendant of this widget has focus.
class FocusListenerContainer extends StatefulWidget {
const FocusListenerContainer({
super.key,
this.border,
this.padding,
this.focusedSide,
this.focusedColor = Colors.black12,
required this.child,
});
/// This is the border that will be used when not focused, and which defines
/// all the attributes except for the [OutlinedBorder.side] when focused.
final OutlinedBorder? border;
/// This is the [BorderSide] that will be used for [border] when the [child]
/// subtree is focused.
final BorderSide? focusedSide;
/// This is the [Color] that will be used as the fill color for the background
/// of the [child] when a descendant widget is focused.
final Color? focusedColor;
/// The padding around the inside of the container.
final EdgeInsetsGeometry? padding;
/// This is defines the subtree to listen to for focus changes.
final Widget child;
@override
State<FocusListenerContainer> createState() => _FocusListenerContainerState();
}
class _FocusListenerContainerState extends State<FocusListenerContainer> {
final FocusNode _focusNode = FocusNode();
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final OutlinedBorder effectiveBorder = widget.border ?? const RoundedRectangleBorder();
return ListenableBuilder(
listenable: _focusNode,
child: Focus(
focusNode: _focusNode,
skipTraversal: true,
canRequestFocus: false,
child: widget.child,
),
builder: (BuildContext context, Widget? child) {
return Container(
padding: widget.padding,
decoration: ShapeDecoration(
color: _focusNode.hasFocus ? widget.focusedColor : null,
shape: effectiveBorder.copyWith(
side: _focusNode.hasFocus ? widget.focusedSide : null,
),
),
child: child,
);
},
);
}
}
class MyField extends StatefulWidget {
const MyField({super.key, required this.label});
final String label;
@override
State<MyField> createState() => _MyFieldState();
}
class _MyFieldState extends State<MyField> {
final TextEditingController controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(child: Text(widget.label)),
Expanded(
flex: 2,
child: TextField(
controller: controller,
onEditingComplete: () {
debugPrint('Field ${widget.label} changed to ${controller.value}');
},
),
),
],
);
}
}
class ListenableBuilderExample extends StatelessWidget {
const ListenableBuilderExample({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('ListenableBuilder Example')),
body: Center(
child: SizedBox(
width: 300,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.only(bottom: 8),
child: MyField(label: 'Company'),
),
FocusListenerContainer(
padding: const EdgeInsets.all(8),
border: const RoundedRectangleBorder(
side: BorderSide(
strokeAlign: BorderSide.strokeAlignOutside,
),
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
// The border side will get wider when the subtree has focus.
focusedSide: const BorderSide(
width: 4,
strokeAlign: BorderSide.strokeAlignOutside,
),
// The container background will change color to this when
// the subtree has focus.
focusedColor: Colors.blue.shade50,
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Owner:'),
MyField(label: 'First Name'),
MyField(label: 'Last Name'),
],
),
),
],
),
),
),
),
),
);
}
}