#flutter #dart #lag
#колебание #дротик #задержка
Вопрос:
Я создаю приложение с каркасом, который содержит:
- A
FutureBuilder
в теле, которое создает aPageView
в качестве дочернего элемента при загрузке данных. BottomNavigationBar
Который синхронизируется сPageView
для более интуитивной навигации.
С точки зрения функциональности все работает нормально. Я могу проводить пальцем влево и вправо между страницами, и currentIndex
они будут правильно обновляться в BottomNavigationBar
, и если я нажму на BottomNavigationBar
элементы, PageView
анимация на правильной странице, как и ожидалось.
Однако… производительность действительно плохая при переключении между страницами, даже в режиме профиля.
После долгих исследований я подтвердил, что задержка присутствует только при обновлении currentIndex
BottomNavigationBar
.
Если я не обновляю BottomNavigationBar
, анимации остаются очень плавными при переключении между страницами, как при пролистывании по PageView
, так и при касании самих BottomNavigationBar
элементов.
Я также могу подтвердить, что это происходит точно так же при использовании setState
и при использовании Provider
. Я действительно надеялся, что это просто setState
неэффективный метод… но не повезло : (
Для setState
реализации, это то, что я делаю:
На PageView
:
onPageChanged: (page) {
setState(() {
_selectedIndex = page;
});
}
В нижней навигационной панели:
onTap: _onTappedBar,
currentIndex: _selectedIndex
и ниже:
void _onTappedBar(int value) {
_pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
setState(() {
_selectedIndex = value;
});
}
Если я закомментирую оба setState
метода, приложение снова станет гладким, как масло, и я также смогу использовать BottomNavigationBar
корректно — он просто не обновляет выбранный элемент.
Достаточно интересно, что если я ТОЛЬКО закомментирую строку внутри обоих setState
методов ( _selectedIndex = page;
и _selectedIndex = value;
), но оставлю методы там, приложение все равно все равно отстает, даже если setState
методы полностью пусты и ничего не обновляют …??
И это Provider
версия:
На PageView
:
onPageChanged: (page) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = page;
}
На BottomBarNavigation
:
onTap: _onTappedBar,
currentIndex: Provider.of<BottomNavigationBarProvider>(context).currentIndex,
и ниже:
void _onTappedBar(int value) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = value;
pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
}
Как уже было сказано, такая же низкая, как и setState
версия : (
Есть идеи о том, что вызывает это отставание и как это исправить? Я действительно не знаю, что еще попробовать.
Комментарии:
1. Кажется, что есть виджет, вызывающий проблему с задержкой при перестроении. Глупый метод: удаляйте один за другим виджет, чтобы увидеть, какой из них вызывает эту проблему, или создайте версию профиля и используйте инструмент разработки, чтобы посмотреть на временную шкалу. Использовали ли вы MediaQuery.of (контекст)? В прошлый раз я столкнулся с проблемой задержки из-за этого.
Ответ №1:
Хорошо, я думаю, что мне удалось решить эту проблему, а также извлечь ценный урок о Flutter!
Я был на правильном пути с дилеммой setState
/ Provider
— вам действительно нужно использовать Provider
(или другое решение для управления состоянием), если вы хотите избежать перестройки всей страницы.
Однако этого недостаточно.
Для того, чтобы использовать модульность этой реализации, вам также необходимо извлечь соответствующий виджет (в данном случае весь BottomNavigationBar
) за пределы основного виджета. Если вы этого не сделаете, кажется, что все на главной странице все равно будет восстановлено, даже если только маленький виджет прослушивает Provider
уведомления.
Итак, теперь это структура моего root_screen
метода build
(упрощенное содержимое тела для удобства чтения):
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: <Widget>[
HomeScreen(),
PerformanceScreen(),
SettingsScreen(),
],
onPageChanged: (page) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = page;
},
);
bottomNavigationBar: MyBottomNavigationBar(onTapped: _onTappedBar),
);
}
Обратите внимание, что bottomNavigationBar:
параметр больше не определен в этом root_screen
. Вместо этого я создал новый класс (a StatelessWidget
) в отдельном файле Dart, который принимает onTapped
функцию в качестве параметра, и я создаю его экземпляр отсюда.
Указанная _onTappedBar
функция определена прямо здесь, на root_screen
, чуть ниже build
метода:
void _onTappedBar(int value) {
Provider.of<BottomNavigationBarProvider>(context, listen: false).currentIndex = value;
_pageController.animateToPage(value, duration: Duration(milliseconds: 500), curve: Curves.ease);
}
И это отдельный файл Dart, содержащий новый MyBottomNavigationBar
класс:
class MyBottomNavigationBar extends StatelessWidget {
@override
const MyBottomNavigationBar({
Key key,
@required this.onTapped,
}) : super(key: key);
final Function onTapped;
Widget build(BuildContext context) {
return BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(
icon: Icon(Icons.trending_up), title: Text('Performance')),
BottomNavigationBarItem(
icon: Icon(Icons.settings), title: Text('Settings')),
],
onTap: onTapped,
currentIndex:
Provider.of<BottomNavigationBarProvider>(context).currentIndex,
);
}
}
Также для полноты картины (и потому, что мне абсолютно необходимо было знать), я попробовал снова использовать setState
подход, сохранив BottomNavigationBar
его в новом отдельном файле. Я хотел понять, достаточно ли простого извлечения виджетов для достижения цели, или вам все еще нужно использовать решение для управления состоянием, несмотря ни на что.
Оказывается … этого было недостаточно! Производительность при использовании setState снова была ужасной, даже несмотря на то, что виджет BottomNavigationBar был извлечен из его собственного файла класса.
Итак, в итоге, чтобы сохранить эффективность вашего приложения и плавность анимации, не забудьте извлечь виджеты и максимально модулировать код Flutter, а также использовать решение для управления состоянием вместо setState
. Кажется, это единственный способ избежать ненужных перерисовок (и ваш код, очевидно, будет намного чище и его будет легче отлаживать).