I have an issue with my FLutter code that I am not able to solve neither to understand why this is happening.
First the code in questoin:
import 'dart:io';import 'dart:ui';import 'package:app/controller/api/api.dart';import 'package:app/data/holder/sidebar_entries_holder.dart';import 'package:app/data/holder/date_holder.dart';import 'package:app/data/holder/user_holder.dart';import 'package:app/data/models/user.dart';import 'package:app/exceptions/login/not_logged_in_exception.dart';import 'package:app/ui/views/login/login_view.dart';import 'package:app/ui/views/responsive/home/mobile/home_mobile_view.dart';import 'package:app/ui/views/responsive/responsive_view.dart';import 'package:app/utils/common/common_functions.dart';import 'package:app/utils/log/log.dart';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'package:provider/provider.dart';import 'package:responsive_builder/responsive_builder.dart';void main() async { WidgetsFlutterBinding.ensureInitialized(); await Log.initialize(); HttpOverrides.global = MyHttpOverrides(); final userHolder = UserHolder(); final dateHOlder = DateHolder(); final sideBarEntriesHolder = SideBarEntriesHolder(); final selectedSideBarEntryHolder = SelectedSideBarEntryHolder(); final window = PlatformDispatcher.instance.views.first; final screenWidth = window.physicalSize.width / window.devicePixelRatio; final screenHeight = window.physicalSize.height / window.devicePixelRatio; final isHandy = screenWidth < 600; if (isHandy) { SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); } else { SystemChrome.setPreferredOrientations([ DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); } try { final User user = await Api.getUserFromLocalDatabase(); userHolder.setUser(user); Api.setUserHolder(userHolder); } catch (e) { if (e is NotLoggedInException) { Log.warning("No user currently logged in. Will be forwarded to the login view"); } else { Log.severe("Error $e will be forwarded to the login view"); } } runApp( MultiProvider( providers: [ ChangeNotifierProvider.value(value: userHolder), ChangeNotifierProvider.value(value: dateHolder), ChangeNotifierProvider.value(value: sideBarEntriesHolder), ChangeNotifierProvider.value(value: selectedSideBarEntryHolder), ], child: const MaterialApp( debugShowCheckedModeBanner: false, home: MyApp(), ), ), );}class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState();}class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) { return const BaseLayout(); }}class BaseLayout extends StatefulWidget { const BaseLayout({Key? key}) : super(key: key); @override _BaseLayoutState createState() => _BaseLayoutState();}class _BaseLayoutState extends State<BaseLayout> { late SelectedSideBarEntryHolder selectedSideBarEntryHolder; late UserHolder userHolder; @override void initState() { super.initState(); selectedSideBarEntryHolder = context.read<SelectedSideBarEntryHolder>(); userHolder = context.read<UserHolder>(); } @override Widget build(BuildContext context) { print("ME TOO"); final user = userHolder.user; if (user != null && user.isAccessTokenValidForRestOfTheDay()) { return Scaffold( body: ResponsiveBuilder(builder: (context, sizingInformation) { if (sizingInformation.isMobile) { return const HomeMobileView(); } else if (sizingInformation.isTablet) { return const ResponsiveView(); } else { return Container(); } }), floatingActionButton: Consumer<SelectedSideBarEntryHolder>( builder: (context, selectedSideBarEntryHolder, child) { return Visibility( visible: selectedSideBarEntryHolder.isNotSelected(), child: FloatingActionButton( onPressed: () { CommonFunctions.showNewDialog(context); }, child: const Icon(Icons.add), ), ); }, ), ); } else { return const LoginView(); } }}import 'package:app/data/holder/sidebar_entries_holder.dart';import 'package:app/data/holder/selected_side_bar_entry_holder.dart';import 'package:app/data/holder/user_holder.dart';import 'package:app/ui/components/appBar/app_bar.dart';import 'package:app/ui/components/dateSelector/date_selector.dart';import 'package:app/ui/components/sidebar/sidebar.dart';import 'package:app/ui/views/responsive/mobile/edit_view.dart';import 'package:app/ui/views/responsive/mobile/info_view.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'package:responsive_builder/responsive_builder.dart';class HomeMobileView extends StatefulWidget { const HomeMobileView({super.key}); @override _HomeMobileView createState() => _HomeMobileView();}class _HomeMobileView extends State<HomeMobileView> { late UserHolder userHolder; late SideBarEntriesHolder sideBarEntriesHolder; @override void initState() { super.initState(); userHolder = context.read<UserHolder>(); sideBarEntriesHolder = context.read<SideBarEntriesHolder>(); } double _calculateSidebarWidth( BuildContext context, DeviceScreenType deviceScreenType) { var screenWidth = MediaQuery.of(context).size.width; if (deviceScreenType == DeviceScreenType.mobile) { return screenWidth; } else if (deviceScreenType == DeviceScreenType.tablet) { return screenWidth * 0.3; } else { return 300; } } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInformation) { return Consumer<SelectedSideBarEntryHolder>( builder: (context, selectedSideBarEntryHolder, child) { if (selectedSideBarEntryHolder.isNotSelected()) { return Scaffold( appBar: const AppBar(), body: Column( children: [ const DateSelector(), Expanded( child: Sidebar( sidebarWidth: _calculateSidebarWidth( context, sizingInformation.deviceScreenType), ), ), ], ), ); } else { if (selectedSideBarEntryHolder.isInEditMode()) { return const EditView(); } else { return const Scaffold( body: InfoView(), ); } } }, ); }, ); }}import 'dart:async';import 'package:app/data/holder/date_holder.dart';import 'package:app/data/holder/selected_side_bar_holder.dart';import 'package:app/ui/modals/error_dialog.dart';import 'package:app/ui/modals/warning_dialog.dart';import 'package:app/utils/log/log.dart';import 'package:app/utils/utils.dart';import 'package:flutter/material.dart';import 'package:app/controller/api/api.dart';import 'package:app/data/holder/sidebar_entries_holder.dart';import 'package:app/data/holder/user_holder.dart';import 'package:app/data/models/Entry.dart';import 'package:app/ui/components/sidebar/sidebar_entry.dart';import 'package:intl/intl.dart';import 'package:provider/provider.dart';class Sidebar extends StatefulWidget { final double sidebarWidth; const Sidebar({ Key? key, required this.sidebarWidth, }) : super(key: key); @override _SidebarState createState() => _SidebarState();}class _SidebarState extends State<Sidebar> { late UserHolder userHolder; late DateHolder dateHolder; late SideBarEntriesHolder sideBarEntriesHolder; late SelectedSideBarEntryHolder selectedSideBarEntryHolder; DateTime? lastUpdate; @override void initState() { super.initState(); userHolder = context.read<UserHolder>(); dateHolder = context.read<DateHolder>(); sideBarEntriesHolder = context.read<SideBarEntriesHolder>(); selectedSideBarEntryHolder = context.read<SelectedSideBarEntryHolder>(); _reloadEntries(); } Future<List<Entry>> _fetchEntries() async { DateHolder dateHolder = Provider.of<DateHolder>(context, listen: false); String formattedDate = DateFormat('yyyy-MM-dd').format(dateHolder.currentDate); List<Entry> entries = []; try { entries = await Api.getEntriesFromRestEndpoint( formattedDate, formattedDate, ); final String formattedCurrentDate = Utils.formatDate(dateHolder.currentDate); sideBarEntriesHolder.setEntries( formattedCurrentDate, entries); return sideBarEntriesHolder.getEntries(formattedCurrentDate); } catch (e) { Log.severe('Error while fetching entries: $e'); return Future.error(e); } } void _reloadEntries() { setState(() { lastUpdate = DateTime.now(); }); } @override Widget build(BuildContext context) { print("I AM CALLED"); return Consumer<DateHolder>( builder: (context, dateHolder, child) { var futureEntries = _fetchEntries()(); return RefreshIndicator( onRefresh: () async { _reloadEntries(); }, child: FutureBuilder<List<Entry>>( future: futureEntries, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return _buildListOnError(snapshot); } else if (snapshot.hasData) { return _buildList(snapshot.data!); } else { return _buildEmptyList(); } }, ), ); }, ); } Widget _buildListOnError( final AsyncSnapshot<List<Entry>> snapshot) { String currentDate = Utils.formatDate(dateHolder.currentDate); bool listNotEmpty = sideBarEntriesHolder.getEntries(currentDate).isNotEmpty; if (snapshot.error is Exception && listNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { WarningDialog.show( context,"Fehler beim Laden der Daten","Wollen Sie es erneut versuchen ?","Ja, nochmal versuchen","Nein, mit den vorhandenen Daten weiterarbeiten", onYesPressed: _reloadEntries); }); return _buildList( sideBarEntriesHolder.get(currentDate)); } else { if (sideBarEntriesHolder.cached.containsKey(currentDate)) { return _buildList( sideBarEntriesHolder.cached[currentDate]!); } else { return _buildEmptyList(); } } } Widget _buildList(List<Entry> entries) { return entries.isEmpty ? _buildEmptyList() : ListView.builder( itemCount: entries.length, itemBuilder: (context, index) { final entry = entries[index]; return SidebarEntry( entry: entry, isSelected: false, onTap: () async { ... }, onLongPress: () { showDeleteDialog(entry); }, ); }, ); } void showDeleteDialog(final Entry entry) async { ... } void showDeleteInformationDialog() { ... } Future<bool> deleteSideBarEntry(final Entry entry) async { ... } Widget _buildEmptyList() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Keine Einträge', style: TextStyle(fontSize: 16), ), ElevatedButton( onPressed: _reloadEntries, // Neuladen der Daten child: const Text('Neu laden'), ), ], ), ); }}import 'package:flutter/material.dart';class CommonFunctions { static Future<void> showNewDialog(BuildContext context) async { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return const AlertDialog( title: Text("My Dialog"), content: SingleChildScrollView( physics: ClampingScrollPhysics(), child: TextField(), )); }, ); }}
My issue is: When I open a dialog with the help of showNewDialog I get a dialog with a TextField. When I focus (or defocus) the textfield the SideBar is rebuilded. Only the Sidebar. Means that I see the output of print("I AM CALLED"); from my SideBar many times but I do not see the output of print("ME TOO"); from my BaseLayout.
I do not understand why my SideBar is rebuilded when focusing a textfield in the dialog. Especially because I am using the Consumer pattern to only rebuild the view when I call the notify method.
Does anyone have an idea why my view keeps rebuilding ?
------------ EDIT ------------The SideBar
The Dialog
When I click in the TextField the focus is set and the KeyBoard is shown which triggers the rebuild (multiple times).
Log output when Opening dialog and seting focus
D/InputMethodManager(15863): showSoftInput() view=io.flutter.embedding.android.FlutterView{d6fdf5d VFE...... .F...... 0,0-1080,2296 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUTI/AssistStructure(15863): Flattened final assist data: 384 bytes, containing 1 windows, 3 viewsD/EGL_emulation(15863): app_time_stats: avg=195.49ms min=2.51ms max=2614.66ms count=14D/InsetsController(15863): show(ime(), fromIme=true)D/EGL_emulation(15863): app_time_stats: avg=483.08ms min=1.31ms max=9613.11ms count=20I/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.461095: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.483933: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.527776: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.557668: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.603329: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.603692: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.604326: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.604595: Notifiying listeners that entries list have changedI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.614896: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.646553: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.689545: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.726604: Get Entries from REST endpointI/flutter (15863): I AM CALLEDI/flutter (15863): [INFO] 2024-04-23 07:49:07.761659: Get Entries from REST endpointI/flutter (15863): [INFO] 2024-04-23 07:49:07.814218: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.814865: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.855985: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.856984: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.869618: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.871666: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.886194: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.886997: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.909218: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.910125: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.939734: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.940123: Notifiying listeners that entrties list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.941196: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.941953: Notifiying listeners that entries list have changedI/flutter (15863): [INFO] 2024-04-23 07:49:07.955544: Start sorting entriesI/flutter (15863): [INFO] 2024-04-23 07:49:07.956654: Notifiying listeners that entries list have changedD/EGL_emulation(15863): app_time_stats: avg=32.01ms min=3.19ms max=282.24ms count=25D/EGL_emulation(15863): app_time_stats: avg=493.36ms min=483.87ms max=500.10ms count=3D/EGL_emulation(15863): app_time_stats: avg=510.94ms min=501.94ms max=519.93ms count=2D/EGL_emulation(15863): app_time_stats: avg=497.79ms min=492.59ms max=500.79ms count=3D/EGL_emulation(15863): app_time_stats: avg=504.19ms min=500.70ms max=507.68ms count=2D/EGL_emulation(15863): app_time_stats: avg=496.64ms min=492.22ms max=501.38ms count=3D/EGL_emulation(15863): app_time_stats: avg=500.09ms min=482.98ms max=515.92ms count=3D/EGL_emulation(15863): app_time_stats: avg=500.91ms min=484.92ms max=516.91ms count=2
Similar behaviour can be observed when defocusing the Dialog.It does not happen if I click on a Button in the Dialog or when I am inputing data into the TextField. Only when focusing/defocusing the TextField.