Created
January 7, 2023 13:02
-
-
Save SuperPenguin/ae58b2cd2fd3d4a95c82ba512b72cfd2 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(App()); | |
} | |
class App extends StatelessWidget { | |
const App({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
home: HomeScreen(), | |
); | |
} | |
} | |
class HomeScreen extends StatefulWidget { | |
const HomeScreen({super.key}); | |
@override | |
State<HomeScreen> createState() => _HomeScreenState(); | |
} | |
class _HomeScreenState extends State<HomeScreen> { | |
late final TextEditingController _textController; | |
late final ValidateController _validateController; | |
@override | |
void initState() { | |
super.initState(); | |
_textController = TextEditingController(); | |
_validateController = ValidateController(textController: _textController); | |
} | |
@override | |
void dispose() { | |
_textController.dispose(); | |
_validateController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: SafeArea( | |
minimum: const EdgeInsets.all(8.0), | |
child: Column( | |
children: [ | |
AnimatedBuilder( | |
animation: _validateController, | |
builder: (context, child) { | |
final snapshot = _validateController.errorTextSnapshot; | |
final errorText = snapshot.data; | |
return TextField( | |
controller: _textController, | |
decoration: InputDecoration( | |
errorText: errorText, | |
), | |
); | |
}, | |
), | |
SizedBox(height: 8.0), | |
AnimatedBuilder( | |
animation: _validateController, | |
builder: (context, child) { | |
final snapshot = _validateController.errorTextSnapshot; | |
final isWaiting = | |
snapshot.connectionState != ConnectionState.done; | |
final hasError = snapshot.data != null; | |
if (isWaiting) { | |
return const CircularProgressIndicator.adaptive(); | |
} | |
return ElevatedButton( | |
onPressed: !hasError ? () {} : null, | |
child: Text('Submit'), | |
); | |
}, | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
class ValidateController with ChangeNotifier { | |
ValidateController({ | |
required TextEditingController textController, | |
}) : _mounted = true, | |
_textController = textController { | |
_textController.addListener(_onTextChanged); | |
_validate(); | |
} | |
bool _mounted; | |
bool get mounted => _mounted; | |
Timer? _timer; | |
void _onTextChanged() { | |
_timer?.cancel(); | |
if (_errorTextSnapshot.connectionState != ConnectionState.waiting) { | |
_errorTextSnapshot = const AsyncSnapshot.waiting(); | |
notifyListeners(); | |
} | |
_timer = Timer(const Duration(milliseconds: 500), _validate); | |
} | |
Future<void> _validate() async { | |
final text = _textController.text; | |
final String? validateResult = await Future.delayed( | |
const Duration(seconds: 1), | |
() => text.length.isOdd ? 'Error!!!' : null, | |
); | |
print(validateResult); | |
final currentText = _textController.text; | |
if (!mounted || currentText != text) return; | |
_errorTextSnapshot = | |
AsyncSnapshot.withData(ConnectionState.done, validateResult); | |
notifyListeners(); | |
} | |
final TextEditingController _textController; | |
AsyncSnapshot<String?> _errorTextSnapshot = const AsyncSnapshot.nothing(); | |
AsyncSnapshot<String?> get errorTextSnapshot => _errorTextSnapshot; | |
@override | |
void dispose() { | |
_timer?.cancel(); | |
_textController.removeListener(_onTextChanged); | |
_mounted = false; | |
super.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment