-
Notifications
You must be signed in to change notification settings - Fork 230
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Mac OS selector handlers for insert tab and cancel op, on Mac s…
- Loading branch information
1 parent
621113d
commit 8aa3231
Showing
9 changed files
with
403 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<component name="ProjectRunConfigurationManager"> | ||
<configuration default="false" name="Super Text Field (debug)" type="FlutterRunConfigurationType" factoryName="Flutter"> | ||
<option name="filePath" value="$PROJECT_DIR$/example/lib/main_super_text_field.dart" /> | ||
<method v="2" /> | ||
</configuration> | ||
</component> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:super_editor/super_editor.dart'; | ||
import 'package:super_editor/super_text_field.dart'; | ||
|
||
/// An app that demos [SuperTextField]. | ||
void main() { | ||
runApp( | ||
MaterialApp( | ||
home: _SuperTextFieldDemo(), | ||
), | ||
); | ||
} | ||
|
||
class _SuperTextFieldDemo extends StatefulWidget { | ||
const _SuperTextFieldDemo(); | ||
|
||
@override | ||
State<_SuperTextFieldDemo> createState() => _SuperTextFieldDemoState(); | ||
} | ||
|
||
class _SuperTextFieldDemoState extends State<_SuperTextFieldDemo> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
body: Center( | ||
child: ConstrainedBox( | ||
constraints: BoxConstraints(maxWidth: 500), | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
crossAxisAlignment: CrossAxisAlignment.stretch, | ||
children: [ | ||
_SingleLineTextField(), | ||
const SizedBox(height: 16), | ||
_MultiLineTextField(), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _SingleLineTextField extends StatefulWidget { | ||
const _SingleLineTextField(); | ||
|
||
@override | ||
State<_SingleLineTextField> createState() => _SingleLineTextFieldState(); | ||
} | ||
|
||
class _SingleLineTextFieldState extends State<_SingleLineTextField> { | ||
final _focusNode = FocusNode(); | ||
final _textController = ImeAttributedTextEditingController( | ||
controller: AttributedTextEditingController(), | ||
); | ||
|
||
@override | ||
void dispose() { | ||
_textController.dispose(); | ||
_focusNode.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return TapRegion( | ||
groupId: "textfields", | ||
onTapOutside: (_) => _focusNode.unfocus(), | ||
child: TextFieldBorder( | ||
focusNode: _focusNode, | ||
borderBuilder: _borderBuilder, | ||
child: SuperTextField( | ||
focusNode: _focusNode, | ||
textController: _textController, | ||
textStyleBuilder: _textStyleBuilder, | ||
hintBuilder: _createHintBuilder("Enter single line text..."), | ||
padding: const EdgeInsets.all(4), | ||
minLines: 1, | ||
maxLines: 1, | ||
inputSource: TextInputSource.ime, | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _MultiLineTextField extends StatefulWidget { | ||
const _MultiLineTextField(); | ||
|
||
@override | ||
State<_MultiLineTextField> createState() => _MultiLineTextFieldState(); | ||
} | ||
|
||
class _MultiLineTextFieldState extends State<_MultiLineTextField> { | ||
final _focusNode = FocusNode(); | ||
final _textController = ImeAttributedTextEditingController( | ||
controller: AttributedTextEditingController(), | ||
); | ||
|
||
@override | ||
void dispose() { | ||
_textController.dispose(); | ||
_focusNode.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return TapRegion( | ||
groupId: "textfields", | ||
onTapOutside: (_) => _focusNode.unfocus(), | ||
child: TextFieldBorder( | ||
focusNode: _focusNode, | ||
borderBuilder: _borderBuilder, | ||
child: SuperTextField( | ||
focusNode: _focusNode, | ||
textController: _textController, | ||
textStyleBuilder: _textStyleBuilder, | ||
hintBuilder: _createHintBuilder("Type some text..."), | ||
padding: const EdgeInsets.all(4), | ||
minLines: 5, | ||
maxLines: 5, | ||
inputSource: TextInputSource.ime, | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
BoxDecoration _borderBuilder(TextFieldBorderState borderState) { | ||
return BoxDecoration( | ||
borderRadius: BorderRadius.circular(4), | ||
border: Border.all( | ||
color: borderState.hasError // | ||
? Colors.red | ||
: borderState.hasFocus | ||
? Colors.blue | ||
: Colors.grey.shade300, | ||
width: borderState.hasError ? 2 : 1, | ||
), | ||
); | ||
} | ||
|
||
TextStyle _textStyleBuilder(Set<Attribution> attributions) { | ||
return defaultTextFieldStyleBuilder(attributions).copyWith( | ||
color: Colors.black, | ||
); | ||
} | ||
|
||
WidgetBuilder _createHintBuilder(String hintText) { | ||
return (BuildContext context) { | ||
return Text( | ||
hintText, | ||
style: TextStyle(color: Colors.grey), | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
super_editor/lib/src/super_textfield/infrastructure/text_field_border.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
|
||
/// A border that displays with different colors based on common text field states, e.g., | ||
/// non-focused, focused, error. | ||
/// | ||
/// The border visuals are chosen by a provided [borderBuilder]. The border state is refreshed, | ||
/// and the [borderBuilder] is re-run, every time focus changes, or the error state changes. | ||
class TextFieldBorder extends StatelessWidget { | ||
const TextFieldBorder({ | ||
super.key, | ||
required this.focusNode, | ||
this.hasError, | ||
required this.borderBuilder, | ||
this.clipBehavior = Clip.none, | ||
required this.child, | ||
}); | ||
|
||
/// The [FocusNode] associated with the [child] text field. | ||
final FocusNode focusNode; | ||
|
||
/// Whether the [child] text field is currently in an error state. | ||
final ValueListenable<bool>? hasError; | ||
|
||
/// Creates a visual border decoration based on a given [TextFieldBorderState]. | ||
final TextFieldBorderBuilder borderBuilder; | ||
|
||
/// Clipping strategy, which defaults to [Clip.none], and can be used to clip [child] | ||
/// text field content when rounded corners are used for the border. | ||
final Clip clipBehavior; | ||
|
||
/// The widget subtree that displays a text field, to which this border applies. | ||
final Widget child; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return ValueListenableBuilder( | ||
valueListenable: hasError ?? ValueNotifier<bool>(false), | ||
builder: (context, hasError, child) { | ||
return ListenableBuilder( | ||
listenable: focusNode, | ||
builder: (context, child) { | ||
return Container( | ||
decoration: borderBuilder(_borderState), | ||
clipBehavior: clipBehavior, | ||
child: child, | ||
); | ||
}, | ||
child: child, | ||
); | ||
}, | ||
child: child, | ||
); | ||
} | ||
|
||
TextFieldBorderState get _borderState => TextFieldBorderState( | ||
hasFocus: focusNode.hasFocus, | ||
hasPrimaryFocus: focusNode.hasPrimaryFocus, | ||
hasError: hasError?.value ?? false, | ||
); | ||
} | ||
|
||
/// Properties that might impact the visual appearance of a text field border. | ||
/// | ||
/// [TextFieldBorder] provides a [TextFieldBorderState] to a [TextFieldBorderBuilder] | ||
/// to create the desired visual border for a text field. | ||
class TextFieldBorderState { | ||
const TextFieldBorderState({ | ||
required this.hasFocus, | ||
required this.hasPrimaryFocus, | ||
required this.hasError, | ||
}); | ||
|
||
final bool hasFocus; | ||
final bool hasPrimaryFocus; | ||
final bool hasError; | ||
} | ||
|
||
typedef TextFieldBorderBuilder = BoxDecoration Function(TextFieldBorderState borderState); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
library super_text_field; | ||
|
||
// The whole text field. | ||
export 'src/super_textfield/super_textfield.dart'; | ||
|
||
// Tools for building new text fields. | ||
export 'src/super_textfield/infrastructure/text_field_border.dart'; |
Oops, something went wrong.