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

Можно ли в пределах одной страницы использовать несколько Scaffold?

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

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

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

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

Можно ли использовать несколько Scaffold на одной странице?

Это частый вопрос на собеседованиях. Ответ: Технически можно, но это BAD PRACTICE в большинстве случаев. Рассмотрю когда это допустимо и почему избегать.

Что такое Scaffold?

Scaffold — это контейнер, который предоставляет стандартный материальный дизайн макет с:

  • AppBar (заголовок)
  • Body (основной контент)
  • FloatingActionButton (плавающая кнопка)
  • BottomNavigationBar (нижняя навигация)
  • Drawer (боковое меню)
class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('My Page')),
      body: Center(child: Text('Hello')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: Icon(Icons.add),
      ),
    );
  }
}

❌ Несколько Scaffold — Плохо

1. Дублирование структуры

// ❌ НЕ ДЕЛАЙ ТАК
class BadMultipleScaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Top AppBar')),
      body: Scaffold(  // Вложенный Scaffold — плохая идея!
        appBar: AppBar(title: Text('Inner AppBar')),
        body: Center(child: Text('Content')),
      ),
    );
  }
}

Проблемы:

  • Два AppBar займут место (смотрится странно)
  • Конфликт обработки событий (back button)
  • Усложнение CSS (стиля), доступности

2. Конфликт FloatingActionButton

// ❌ Какой FAB будет виден?
class BadFABs extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () => print('Outer FAB'),
        child: Icon(Icons.add),
      ),
      body: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () => print('Inner FAB'),
          child: Icon(Icons.edit),
        ),
        body: Center(child: Text('Content')),
      ),
    );
  }
}

// Результат: конфликты и непредсказуемое поведение

3. Проблемы с навигацией

// ❌ Back button не работает правильно
class BadNavigation extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Outer Page'),
        leading: BackButton(),  // Какой Scaffold он закроет?
      ),
      body: Scaffold(
        appBar: AppBar(title: Text('Inner Page')),
        body: Center(child: Text('Confusing UI')),
      ),
    );
  }
}

✅ Правильные подходы

1. Один Scaffold, несколько виджетов в body

// ✅ ПРАВИЛЬНО
class ProperPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('My Page')),
      body: SingleChildScrollView(
        child: Column(
          children: [
            // Первая секция
            Container(
              padding: EdgeInsets.all(16),
              child: Text('Section 1'),
            ),
            // Вторая секция
            Container(
              padding: EdgeInsets.all(16),
              child: Text('Section 2'),
            ),
            // Третья секция
            Container(
              padding: EdgeInsets.all(16),
              child: Text('Section 3'),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => print('One FAB for all'),
        child: Icon(Icons.add),
      ),
    );
  }
}

2. Использование TabBar для разных экранов

// ✅ ПРАВИЛЬНО — Один Scaffold с TabBarView
class TabbedPage extends StatefulWidget {
  @override
  State<TabbedPage> createState() => _TabbedPageState();
}

class _TabbedPageState extends State<TabbedPage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;
  
  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }
  
  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Tabbed Page'),
        bottom: TabBar(
          controller: _tabController,
          tabs: [
            Tab(text: 'Tab 1'),
            Tab(text: 'Tab 2'),
            Tab(text: 'Tab 3'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          Center(child: Text('Content 1')),
          Center(child: Text('Content 2')),
          Center(child: Text('Content 3')),
        ],
      ),
    );
  }
}

3. Несколько страниц через Navigator (правильный способ)

// ✅ ПРАВИЛЬНО — Разные Scaffold на разных страницах
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Переход на другую страницу с собственным Scaffold
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => DetailsPage()),
            );
          },
          child: Text('Go to Details'),
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(  // Другой Scaffold для другой страницы
      appBar: AppBar(
        title: Text('Details'),
        automaticallyImplyLeading: true,  // Back button
      ),
      body: Center(child: Text('Details content')),
    );
  }
}

✅ РЕДКИЕ случаи когда несколько Scaffold допустимы

1. BottomSheet с собственным контентом

// ⚠️ Условно допустимо — но лучше использовать showModalBottomSheet
class PageWithBottomSheet extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Main Page')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // ✅ Правильный способ — showModalBottomSheet
            showModalBottomSheet(
              context: context,
              builder: (context) => Container(
                height: 300,
                child: Center(child: Text('Bottom Sheet')),
              ),
            );
          },
          child: Text('Show Bottom Sheet'),
        ),
      ),
    );
  }
}

2. Dialog с собственным Scaffold (очень редко)

// ⚠️ Очень редкий случай — Full screen dialog
class PageWithDialog extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Main')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // ✅ Лучше использовать showGeneralDialog или Navigator
            Navigator.of(context, rootNavigator: true).push(
              MaterialPageRoute(
                fullscreenDialog: true,  // Полноэкранный диалог
                builder: (context) => Scaffold(
                  appBar: AppBar(title: Text('Dialog')),
                  body: Center(child: Text('Dialog content')),
                ),
              ),
            );
          },
          child: Text('Show Dialog'),
        ),
      ),
    );
  }
}

Сравнение подходов

ПодходИспользуй когдаПример
Один Scaffold + ColumnНесколько секций на одной страницеПрофиль пользователя
Scaffold + TabBarViewНесколько независимых вкладокGmail (Mail, Contacts, etc)
Navigator + разные ScaffoldРазные экраны приложенияHome → Details → Settings
showModalBottomSheetВременная информацияФильтры, меню
fullscreenDialogПолноэкранный диалогСоздание объекта

Практический пример: Правильная архитектура

// ✅ Правильная структура
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  int _currentIndex = 0;
  
  final _pages = [
    HomePage(),
    SearchPage(),
    ProfilePage(),
  ];
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // Один главный Scaffold
      appBar: AppBar(title: Text('My App')),
      body: _pages[_currentIndex],  // Разные виджеты, но один Scaffold
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
      ),
    );
  }
}

class HomePage extends Widget {
  // ✅ Нет собственного Scaffold!
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(children: [
        // Контент
      ]),
    );
  }
}

Резюме

Вопрос: Можно ли использовать несколько Scaffold?

Ответ: Технически можно, но НЕ НУЖНО в 99% случаев.

Правило:

  • Одна страница = один Scaffold
  • Несколько экранов = несколько Scaffold (на разных страницах через Navigator)
  • Несколько секций = один Scaffold + Column/ListView в body
  • Modal content = showModalBottomSheet / showDialog (без Scaffold)

Best Practice:

  • Используй один Scaffold для основного макета
  • Разные страницы приложения = разные Scaffold через Navigator
  • Модальные окна через showModalBottomSheet, showDialog, fullscreenDialog
  • Вложенные Scaffold = признак неправильной архитектуры
Можно ли в пределах одной страницы использовать несколько Scaffold? | PrepBro