Reputation: 1442
For debugging purposes, I had put a print statement inside the build method and I noticed that the whole widget was rebuilding multiple times when the TextFormField was tapped.
The keyboard opens up, and then immediately closes.
This is my code:
class LoginScreen extends StatelessWidget {
LoginScreen({super.key});
static Route<void> route() {
return MaterialPageRoute<void>(builder: (_) => LoginScreen());
}
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _selectUrl = TextEditingController();
@override
Widget build(BuildContext context) {
if (!kReleaseMode) {
_emailController.text = '';
_passwordController.text = '';
_selectUrl.text = baseUrl;
}
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.fromLTRB(
20.0,
),
child: Image.asset(
'assets/images/logo.png',
color: isDarkMode(context)
? Theme.of(context).colorScheme.primary
: null,
),
),
),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'Enter your email',
),
),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
hintText: 'Enter your password',
),
obscureText: true,
),
BlocConsumer<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state is AuthenticationFailureState) {
_emailController.text = state.fields['email'] ?? '';
_passwordController.text = state.fields['password'] ?? '';
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Login failed'),
content: Text(
state.errorMessage,
textAlign: TextAlign.center,
),
icon: const Icon(
Icons.error,
color: Colors.red,
));
});
} else if (state is AuthenticationSuccessState) {
context
.read<CalendarDataBloc>()
.add(FetchCalendarEvents());
context.read<HomeBloc>().add(FetchCheckedInEvents());
Navigator.pushReplacementNamed(context, '/home');
}
},
builder: (context, state) {
return state is AuthenticationLoadingState &&
(state).isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SizedBox(
child: FilledButton(
onPressed: () {
BlocProvider.of<AuthenticationBloc>(context)
.add(
LoginUser(
_emailController.text.trim(),
_passwordController.text.trim(),
),
);
},
child: const Text(
'Login',
style: TextStyle(
fontSize: 20,
),
),
),
);
},
),
],
),
),
),
),
);
}
}
This is my main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SecureStorageService.instance;
await HiveStorageService.init();
final ThemeMode initialThemeMode = HiveStorageService().getThemeMode();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
if (Platform.isAndroid) {
await FirebaseNotifications().initNotifications();
}
if (!kDebugMode) {
FlutterError.onError = (errorDetails) {
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
await SentryFlutter.init(
(options) {
options.dsn =
'';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
options.environment = kReleaseMode ? 'production' : 'profile';
},
appRunner: () => runApp(MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthenticationBloc()..add(const AppStarted()),
),
BlocProvider<HomeBloc>(create: (context) => HomeBloc()),
BlocProvider<CalendarBloc>(create: (context) => CalendarBloc()),
BlocProvider<CalendarDataBloc>(
create: (context) => CalendarDataBloc()),
BlocProvider(
create: (_) => ThemeCubit()..setThemeMode(initialThemeMode)),
],
child: const MyApp(),
)),
);
} else {
runApp(MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthenticationBloc()..add(const AppStarted()),
),
BlocProvider<HomeBloc>(create: (context) => HomeBloc()),
BlocProvider<CalendarBloc>(create: (context) => CalendarBloc()),
BlocProvider<CalendarDataBloc>(create: (context) => CalendarDataBloc()),
BlocProvider(
create: (_) => ThemeCubit()..setThemeMode(initialThemeMode)),
],
child: const MyApp(),
));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const platform = MethodChannel('in.co.ezerx.sales_iq/check');
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: getTheme(context, currentTheme: context.watch<ThemeCubit>().state),
home: Platform.isAndroid
? FutureBuilder<bool>(
future: _isDeveloperOptionsEnabled(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
} else if (snapshot.hasError) {
return const Scaffold(
body: Center(
child: Text('Error checking developer options'),
),
);
} else if (snapshot.data == true && kReleaseMode) {
return const Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Developer options are enabled'),
Text('Please disable developer options to continue'),
],
),
),
);
} else {
return BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationSuccessState) {
return const HomeScreen();
} else {
return LoginScreen();
}
});
}
})
: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationSuccessState) {
return const HomeScreen();
} else {
return LoginScreen();
}
}),
onGenerateRoute: (setting) => _generateRoute(setting),
);
}
Future<bool> _isDeveloperOptionsEnabled() async {
try {
final bool result = await platform.invokeMethod('checkDevOptions');
return result;
} on PlatformException catch (e) {
errorPrint(e.message);
return false;
}
}
Route _generateRoute(RouteSettings settings) {
Widget screen;
switch (settings.name) {
case '/':
screen = LoginScreen();
break;
case '/home':
screen = const HomeScreen();
break;
case '/settings':
screen = const SettingsScreen();
break;
case '/checkin':
screen = const Checkin();
break;
case '/checkout':
screen = Checkout();
break;
case '/prospect':
screen = Prospect();
break;
case '/create_event':
screen = CreateEvent();
break;
case '/reschedule':
screen = Reschedule();
break;
case '/prospect_list':
screen = ProspectList();
break;
case '/profile':
screen = const Profile();
break;
case '/dashboard':
screen = const Dashboard();
break;
case '/cumulative_dashboard':
screen = const CumulativeDashboard();
break;
default:
screen = LoginScreen();
}
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => screen,
settings: settings,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
}
Upvotes: -1
Views: 78
Reputation: 154
Convert your stateless widgets into State full widgets override init method. Remove your build method code and covert to like this.
@override
initState() {
if (!kReleaseMode) {
_emailController.text = '';
_passwordController.text = '';
_selectUrl.text = baseUrl;
},}
Upvotes: 0
Reputation: 177
Check Bloc State Changes If your AuthenticationBloc is emitting new states frequently (such as AuthenticationLoadingState), it could be triggering rebuilds too often.
Upvotes: 0