Skip to content

Instantly share code, notes, and snippets.

@umaqs
Last active May 5, 2021 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save umaqs/499509f07996ce13dcf8375215832b9c to your computer and use it in GitHub Desktop.
Save umaqs/499509f07996ce13dcf8375215832b9c to your computer and use it in GitHub Desktop.
Centered TextField test
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _mediumCenteredHelperText;
String _mediumCenteredErrorText;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: context.allPadding16,
child: Text(
'Type \'error\' to display an error message\nType \'help\' to display the helper text',
),
),
context.vBox24,
Padding(
padding: context.paddingEdges16,
child: GhCenteredTextField(
autocorrect: false,
labelText: 'Label',
hintText: 'Your First Name',
errorText: _mediumCenteredErrorText,
helperText: _mediumCenteredHelperText,
onChanged: (text) {
setState(() {
_mediumCenteredErrorText =
text.toLowerCase().contains('error')
? 'Error text'
: null;
_mediumCenteredHelperText =
text.toLowerCase().contains('help')
? 'Helper text'
: null;
});
},
textCapitalization: TextCapitalization.none,
),
),
],
),
),
);
}
}
class GhCenteredTextField extends StatefulWidget {
const GhCenteredTextField({
Key key,
this.labelText,
this.hintText,
this.helperText,
this.errorText,
this.initialValue,
this.controller,
this.onChanged,
this.onEditingComplete,
this.inputActionType,
this.keyboardType,
this.inputFormatters,
this.textCapitalization = TextCapitalization.sentences,
this.autocorrect = true,
this.autofocus = false,
this.isLarge = false,
this.obscureText = false,
this.showClearButton = false,
}) : assert(autocorrect != null),
assert(autofocus != null),
assert(isLarge != null),
assert(obscureText != null),
assert(showClearButton != null),
super(key: key);
final String labelText;
final String hintText;
final String helperText;
final String errorText;
final String initialValue;
final TextEditingController controller;
final ValueChanged<String> onChanged;
final VoidCallback onEditingComplete;
final TextInputAction inputActionType;
final TextInputType keyboardType;
final List<TextInputFormatter> inputFormatters;
final TextCapitalization textCapitalization;
final bool autocorrect;
final bool autofocus;
final bool isLarge;
final bool obscureText;
final bool showClearButton;
@override
_GhCenteredTextFieldState createState() => _GhCenteredTextFieldState();
}
class _GhCenteredTextFieldState extends State<GhCenteredTextField> {
TextEditingController _controller;
bool _hasFocus;
@override
void initState() {
super.initState();
_controller = widget.controller ?? TextEditingController();
if (widget.initialValue != null && widget.initialValue.isNotEmpty) {
_controller.text = widget.initialValue;
}
_controller.addListener(_onTyped);
_hasFocus = widget.autofocus;
}
void _onTyped() {
setState(() {});
}
void _onFocusChanged(bool hasFocus) {
setState(() {
_hasFocus = hasFocus;
});
}
@override
void dispose() {
_controller.removeListener(_onTyped);
if (widget.controller == null) {
_controller.dispose();
}
super.dispose();
}
bool get _displayClearIcon {
return (_hasFocus || widget.showClearButton) && _controller.text.isNotEmpty;
}
bool get _hasLabel => widget.labelText != null;
bool get _hasError => widget.errorText != null;
bool get _hasHelperText => widget.helperText != null;
// Widget get _suffixIcon {
// return AnimatedOpacity(
// opacity: _displayClearIcon ? 1 : 0,
// duration: kToggleableAnimDuration,
// child: IconButton(
// onPressed: () {
// _controller.clear();
// widget.onChanged?.call('');
// },
// icon: Icon(
// Icons.clear,
// color: Colors.grey,
// ),
// ),
// );
// }
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textStyle =
widget.isLarge ? theme.textTheme.headline2 : theme.textTheme.headline3;
final hintStyle = textStyle.copyWith(color: Colors.grey);
final labelStyle = theme.textTheme.headline4.copyWith(
color: _hasError ? Colors.orangeAccent : null,
);
final footerStyle = theme.textTheme.headline4.copyWith(height: 2);
return Theme(
data: context.centeredTextFieldThemeData(hasError: _hasError),
child: Column(
children: [
if (_hasLabel)
Text(
widget.labelText,
style: labelStyle,
textAlign: TextAlign.center,
),
context.vBox32,
FocusScope(
onFocusChange: _onFocusChanged,
child: IntrinsicWidth(
child: TextField(
textCapitalization: widget.textCapitalization,
autofocus: widget.autofocus,
controller: _controller,
obscureText: widget.obscureText,
style: textStyle,
textAlign: _controller.text.isEmpty
? TextAlign.left
: TextAlign.center,
onChanged: widget.onChanged,
onEditingComplete: widget.onEditingComplete,
textInputAction: widget.inputActionType,
inputFormatters: widget.inputFormatters,
keyboardType: widget.keyboardType,
decoration: InputDecoration(
labelText: widget.hintText,
hintText: widget.hintText,
labelStyle: hintStyle,
hintStyle: hintStyle,
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
contentPadding: context.textFieldContentPadding,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none),
),
),
),
Divider(
color: _hasError ? Colors.orangeAccent : Colors.blue,
height: 4,
thickness: 2,
),
context.vBox16,
if (_hasError)
Text(
widget.errorText,
style: footerStyle.copyWith(color: Colors.orangeAccent),
textAlign: TextAlign.center,
maxLines: 4,
)
else if (_hasHelperText)
Text(
widget.helperText,
style: footerStyle,
textAlign: TextAlign.center,
maxLines: 4,
),
],
),
);
}
}
extension GhThemeBuildContextExtension on BuildContext {
ThemeData centeredTextFieldThemeData({bool hasError}) {
final data = Theme.of(this);
return data.copyWith(
primaryColor: data.primaryColor,
hintColor: Colors.grey,
textSelectionTheme: data.textSelectionTheme.copyWith(
cursorColor: data.primaryColor,
),
// inputDecorationTheme: InputDecorationTheme(
// contentPadding: EdgeInsets.zero,
// border: InputBorder.none,
// enabledBorder: InputBorder.none,
// focusedBorder: InputBorder.none,
// errorBorder: InputBorder.none,
// focusedErrorBorder: InputBorder.none,
// ),
);
}
/// skip any of the [min] and [max] params to avoid clamping
///
/// context.textScaleFactorClamped() will result in MediaQuery.of(context).textScaleFactor
double textScaleFactorClamped({double min, double max}) {
final textScaleFactor = MediaQuery.of(this).textScaleFactor;
if (min == null && max == null) {
return textScaleFactor;
}
if (max == null) {
return textScaleFactor.clamp(min, min + textScaleFactor) as double;
}
if (min == null) {
return textScaleFactor.clamp(0, max) as double;
}
return textScaleFactor.clamp(min, max) as double;
}
double get size40 => 40;
double get loadingIndicatorSize => 40;
double get badgeSize32 => 32;
double get iconSize16 => 16;
double get iconSize24 => 24;
double get iconSize32 => 32;
double get iconSizeBioLogin => 48;
double get iconSize56 => 56;
double get iconSize64 => 64;
double get iconSize112 => 112;
SizedBox get vBoxBetweenButtons => const SizedBox(height: 16);
SizedBox get vBoxBottom => const SizedBox(height: 24);
SizedBox get vBox10PctScreenHeight {
final height = MediaQuery.of(this).size.height;
return SizedBox(height: height * 0.10);
}
SizedBox get vBoxBottomDashboardTabWithFloatingActions =>
const SizedBox(height: 80);
SizedBox get vBox4 => const SizedBox(height: 4);
SizedBox get vBox8 => const SizedBox(height: 8);
SizedBox get vBox12 => const SizedBox(height: 12);
SizedBox get vBox16 => const SizedBox(height: 16);
SizedBox get vBox24 => const SizedBox(height: 24);
SizedBox get vBox32 => const SizedBox(height: 32);
SizedBox get vBox40 => const SizedBox(height: 40);
SizedBox get vBox56 => const SizedBox(height: 56);
SizedBox get hBoxBetweenButtons => const SizedBox(width: 16);
SizedBox get hBox4 => const SizedBox(width: 4);
SizedBox get hBox8 => const SizedBox(width: 8);
SizedBox get hBox12 => const SizedBox(width: 12);
SizedBox get hBox16 => const SizedBox(width: 16);
SizedBox get hBox24 => const SizedBox(width: 24);
SizedBox get hBox32 => const SizedBox(width: 32);
EdgeInsetsGeometry get allPadding4 => const EdgeInsets.all(4);
EdgeInsetsGeometry get allPadding8 => const EdgeInsets.all(8);
EdgeInsetsGeometry get allPadding16 => const EdgeInsets.all(16);
EdgeInsetsGeometry get allPadding24 => const EdgeInsets.all(24);
EdgeInsetsGeometry get textFieldContentPadding =>
const EdgeInsets.only(bottom: 8);
EdgeInsetsGeometry get tabPaddingEdges =>
const EdgeInsets.symmetric(horizontal: 4);
EdgeInsetsGeometry get paddingEdges8 =>
const EdgeInsets.symmetric(horizontal: 8);
EdgeInsetsGeometry get paddingLeft16 => const EdgeInsets.only(left: 16);
EdgeInsetsGeometry get paddingVertical16 =>
const EdgeInsets.symmetric(vertical: 16);
EdgeInsetsGeometry get paddingEdges16 =>
const EdgeInsets.symmetric(horizontal: 16);
EdgeInsetsGeometry get paddingEdges24 =>
const EdgeInsets.symmetric(horizontal: 24);
double get edge16 => 16;
}
const kToggleableAnimDuration = Duration(milliseconds: 150);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment