← Назад к вопросам

Как обрабатывать пользовательский ввод во Flutter?

1.2 Junior🔥 192 комментариев
#Flutter виджеты

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обработка пользовательского ввода во Flutter

Обработка ввода — это фундаментальная часть любого приложения. Рассмотрю полный спектр: от текстовых полей до жестов.

1. TextField — текстовое поле

class TextInputExample extends StatefulWidget {
  @override
  State<TextInputExample> createState() => _TextInputExampleState();
}

class _TextInputExampleState extends State<TextInputExample> {
  late TextEditingController controller;
  String? _error;
  
  @override
  void initState() {
    super.initState();
    controller = TextEditingController();
    
    // Слушать изменения
    controller.addListener(() {
      setState(() {
        _error = _validateEmail(controller.text);
      });
    });
  }
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
  
  String? _validateEmail(String email) {
    if (email.isEmpty) return 'Email обязателен';
    if (!email.contains('@')) return 'Неверный email';
    return null;
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TextField(
        controller: controller,
        decoration: InputDecoration(
          labelText: 'Email',
          hintText: 'введите email',
          errorText: _error,
          border: OutlineInputBorder(),
          prefixIcon: Icon(Icons.email),
          suffixIcon: controller.text.isNotEmpty
              ? IconButton(
                  icon: Icon(Icons.clear),
                  onPressed: () => controller.clear(),
                )
              : null,
        ),
        keyboardType: TextInputType.emailAddress,
        textInputAction: TextInputAction.next,
      ),
    );
  }
}

2. Обработка различных типов ввода

class FormInputsExample extends StatefulWidget {
  @override
  State<FormInputsExample> createState() => _FormInputsExampleState();
}

class _FormInputsExampleState extends State<FormInputsExample> {
  final formKey = GlobalKey<FormState>();
  final nameController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();
  DateTime? selectedDate;
  String? selectedOption;
  bool agreedToTerms = false;
  
  @override
  void dispose() {
    nameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Форма')),
      body: Form(
        key: formKey,
        child: ListView(
          padding: EdgeInsets.all(16),
          children: [
            // Текстовое поле
            TextFormField(
              controller: nameController,
              decoration: InputDecoration(labelText: 'Имя'),
              validator: (value) {
                if (value?.isEmpty ?? true) return 'Введите имя';
                return null;
              },
            ),
            SizedBox(height: 16),
            
            // Email
            TextFormField(
              controller: emailController,
              decoration: InputDecoration(labelText: 'Email'),
              keyboardType: TextInputType.emailAddress,
              validator: (value) {
                if (value?.isEmpty ?? true) return 'Введите email';
                if (!value!.contains('@')) return 'Неверный email';
                return null;
              },
            ),
            SizedBox(height: 16),
            
            // Пароль
            TextFormField(
              controller: passwordController,
              decoration: InputDecoration(
                labelText: 'Пароль',
                suffixIcon: IconButton(
                  icon: Icon(Icons.visibility),
                  onPressed: () => setState(() {}),
                ),
              ),
              obscureText: true,
              validator: (value) {
                if (value?.isEmpty ?? true) return 'Введите пароль';
                if ((value?.length ?? 0) < 6) return 'Минимум 6 символов';
                return null;
              },
            ),
            SizedBox(height: 16),
            
            // Выпадающий список
            DropdownButtonFormField<String>(
              value: selectedOption,
              items: ['Option 1', 'Option 2', 'Option 3']
                  .map((e) => DropdownMenuItem(value: e, child: Text(e)))
                  .toList(),
              onChanged: (value) => setState(() => selectedOption = value),
              decoration: InputDecoration(labelText: 'Выберите опцию'),
              validator: (value) {
                if (value == null) return 'Выберите опцию';
                return null;
              },
            ),
            SizedBox(height: 16),
            
            // Дата
            GestureDetector(
              onTap: () async {
                final date = await showDatePicker(
                  context: context,
                  initialDate: DateTime.now(),
                  firstDate: DateTime(1900),
                  lastDate: DateTime.now(),
                );
                if (date != null) {
                  setState(() => selectedDate = date);
                }
              },
              child: InputDecorator(
                decoration: InputDecoration(labelText: 'Дата рождения'),
                child: Text(
                  selectedDate?.toString().split(' ')[0] ?? 'Выберите дату',
                ),
              ),
            ),
            SizedBox(height: 16),
            
            // Checkbox
            CheckboxListTile(
              title: Text('Согласен с условиями'),
              value: agreedToTerms,
              onChanged: (value) => setState(() => agreedToTerms = value ?? false),
            ),
            SizedBox(height: 24),
            
            // Кнопка отправки
            ElevatedButton(
              onPressed: () {
                if (formKey.currentState!.validate() && agreedToTerms) {
                  print('Form submitted');
                  print('Name: ${nameController.text}');
                  print('Email: ${emailController.text}');
                }
              },
              child: Text('Отправить'),
            ),
          ],
        ),
      ),
    );
  }
}

3. Обработка жестов (Gesture)

class GestureInputExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Tap
            GestureDetector(
              onTap: () => print('Тап!'),
              onDoubleTap: () => print('Двойной тап!'),
              onLongPress: () => print('Длинный нажим!'),
              child: Container(
                width: 200,
                height: 200,
                color: Colors.blue,
                child: Center(child: Text('Тап меня')),
              ),
            ),
            SizedBox(height: 32),
            
            // Drag
            DraggableExample(),
            SizedBox(height: 32),
            
            // Swipe
            SwipeDetectorExample(),
          ],
        ),
      ),
    );
  }
}

// Перетаскивание
class DraggableExample extends StatefulWidget {
  @override
  State<DraggableExample> createState() => _DraggableExampleState();
}

class _DraggableExampleState extends State<DraggableExample> {
  Offset position = Offset.zero;
  
  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: position.dx,
      top: position.dy,
      child: GestureDetector(
        onPanUpdate: (details) {
          setState(() {
            position += details.delta;
          });
        },
        child: Container(
          width: 100,
          height: 100,
          color: Colors.red,
          child: Center(child: Text('Тащи меня')),
        ),
      ),
    );
  }
}

// Свайпы
class SwipeDetectorExample extends StatefulWidget {
  @override
  State<SwipeDetectorExample> createState() => _SwipeDetectorExampleState();
}

class _SwipeDetectorExampleState extends State<SwipeDetectorExample> {
  String direction = '';
  
  void _handleSwipe(DragEndDetails details) {
    const double swipeThreshold = 200.0;
    
    if (details.velocity.pixelsPerSecond.dx.abs() > swipeThreshold) {
      if (details.velocity.pixelsPerSecond.dx > 0) {
        setState(() => direction = 'Свайп вправо');
      } else {
        setState(() => direction = 'Свайп влево');
      }
    } else if (details.velocity.pixelsPerSecond.dy.abs() > swipeThreshold) {
      if (details.velocity.pixelsPerSecond.dy > 0) {
        setState(() => direction = 'Свайп вниз');
      } else {
        setState(() => direction = 'Свайп вверх');
      }
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanEnd: _handleSwipe,
      child: Container(
        width: 200,
        height: 100,
        color: Colors.green,
        child: Center(child: Text(direction.isNotEmpty ? direction : 'Свайпай')),
      ),
    );
  }
}

4. Обработка клавиатуры

class KeyboardInputExample extends StatefulWidget {
  @override
  State<KeyboardInputExample> createState() => _KeyboardInputExampleState();
}

class _KeyboardInputExampleState extends State<KeyboardInputExample> {
  final controller = TextEditingController();
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () => FocusScope.of(context).unfocus(),  // Скрыть клавиатуру
        child: Column(
          children: [
            TextField(
              controller: controller,
              decoration: InputDecoration(labelText: 'Введите текст'),
              onSubmitted: (value) {
                print('Отправлено: $value');
                FocusScope.of(context).unfocus();
              },
              textInputAction: TextInputAction.send,
            ),
            SizedBox(height: 16),
            
            // Кнопка для скрытия клавиатуры
            ElevatedButton(
              onPressed: () {
                FocusScope.of(context).unfocus();
              },
              child: Text('Скрыть клавиатуру'),
            ),
          ],
        ),
      ),
    );
  }
}

5. Валидация формы

class FormValidationExample extends StatefulWidget {
  @override
  State<FormValidationExample> createState() => _FormValidationExampleState();
}

class _FormValidationExampleState extends State<FormValidationExample> {
  final formKey = GlobalKey<FormState>();
  
  String? validateUsername(String? value) {
    if (value?.isEmpty ?? true) return 'Username обязателен';
    if (value!.length < 3) return 'Минимум 3 символа';
    if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(value)) {
      return 'Только буквы, цифры и подчёркивание';
    }
    return null;
  }
  
  String? validatePassword(String? value) {
    if (value?.isEmpty ?? true) return 'Пароль обязателен';
    if (value!.length < 8) return 'Минимум 8 символов';
    if (!RegExp(r'[A-Z]').hasMatch(value)) return 'Требуется заглавная буква';
    if (!RegExp(r'[0-9]').hasMatch(value)) return 'Требуется цифра';
    return null;
  }
  
  String? validateEmail(String? value) {
    if (value?.isEmpty ?? true) return 'Email обязателен';
    const emailRegex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';
    if (!RegExp(emailRegex).hasMatch(value!)) return 'Неверный email';
    return null;
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Валидация')),
      body: Form(
        key: formKey,
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            children: [
              TextFormField(
                decoration: InputDecoration(labelText: 'Username'),
                validator: validateUsername,
              ),
              SizedBox(height: 16),
              TextFormField(
                decoration: InputDecoration(labelText: 'Email'),
                validator: validateEmail,
              ),
              SizedBox(height: 16),
              TextFormField(
                decoration: InputDecoration(labelText: 'Пароль'),
                obscureText: true,
                validator: validatePassword,
              ),
              SizedBox(height: 32),
              ElevatedButton(
                onPressed: () {
                  if (formKey.currentState!.validate()) {
                    print('Форма валидна!');
                  }
                },
                child: Text('Отправить'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Чеклист для production

  • Используй TextEditingController и не забывай dispose()
  • Валидируй ввод на клиенте
  • Показывай ошибки пользователю
  • Обработай всё вручную введённые данные на сервере
  • Используй TextFormField с GlobalKey<FormState> для форм
  • Скрывай клавиатуру когда она не нужна
  • Тестируй с разными длинами текста
  • Используй правильный keyboardType (email, number и т.д.)
  • Обработай пустой ввод
  • Покажи feedback (снэки, диалоги) о результатах
Как обрабатывать пользовательский ввод во Flutter? | PrepBro