ConvexAppBar does not work on Flutter Web - flutter

I've built an app for Android and iOS using the ConvexAppBar and it worked perfectly. Now I need to create a web version for this app. For that I changed the flutter channel to beta and updated all dependecies. But the app never works when the ConvexAppBar is shown in screen,I get the following error:
The following assertion was thrown building StyleProvider:
DefaultTabController.of() was called with a context that does not
contain a DefaultTabController widget. No DefaultTabController widget
ancestor could be found starting from the context that was passed to
DefaultTabController.of(). This can happen because you are using a
widget that looks for a DefaultTabController ancestor, but no such
ancestor exists.
To be able to change the tabs without clicking on the menu items I've created a GlobalKey for ConvexAppBar and ScaffoldState:
final GlobalKey<ConvexAppBarState> appBarKey = GlobalKey<ConvexAppBarState>();
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
The ConvexAppBar is set in the bottomNavigationBar:
class HomePageView extends StatelessWidget {
const HomePageView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final cubit = BlocProvider.of<HomePageCubit>(context);
var translation =
Map<String, String>.from(globals.translation['HomePageTranslation']);
return Scaffold(
key: scaffoldKey,
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const HeaderMenu(),
DrawerContent(cubit, translation),
],
),
),
onDrawerChanged: (bool value) {
cubit.onDrawerChanged(value);
},
body: BlocConsumer<HomePageCubit, HomePageState>(
listenWhen: (previous, current) => current is OpenAddTaskState,
listener: (context, state) {
if (state is OpenAddTaskState) {
goToAddTaskPage(context);
}
},
buildWhen: (previous, current) =>
current is InitHomePageState ||
current is LoadingTasksHomePageState ||
current is LoadedReceivedTasksState ||
current is LoadedSentTasksState ||
current is LoadedNotificationsState,
builder: (context, state) {
if (state is InitHomePageState ||
state is LoadingTasksHomePageState) {
return ProgressView(
message: translation['loadingHomePageText'] ?? '',
);
}
if (state is LoadedReceivedTasksState) {
return TaskListReceivedContainer(
[
translation['inboxText'] ?? '',
translation['toDoText'] ?? '',
translation['refusedText'] ?? '',
translation['finishedText'] ?? '',
translation['followingText'] ?? '',
'Menu'
],
'received',
);
}
if (state is LoadedSentTasksState) {
return TaskListSentContainer(
[
translation['pendingText'] ?? '',
translation['acceptedText'] ?? '',
translation['refusedSentText'] ?? '',
translation['finishedSentText'] ?? '',
],
'sent',
);
}
if (state is LoadedNotificationsState) {
return const NotificationsContainer();
}
return const ErrorView('Unknown error');
},
),
bottomNavigationBar: BlocBuilder<HomePageCubit, HomePageState>(
builder: (context, state) {
if (state is LoadedSentTasksState) {
return convexMenu(context, const Color(0xffa98e47),
context.read<HomePageCubit>().notifications, translation);
}
return convexMenu(context, const Color(0xff00c9ff),
context.read<HomePageCubit>().notifications, translation);
},
),
);
}
void goToAddTaskPage(BuildContext blocContext) {
Navigator.of(blocContext)
.push(
MaterialPageRoute(
builder: (context) => AddTaskContainer(Task()),
),
)
.then(
(value) => blocContext.read<HomePageCubit>().reloadTasks(),
);
}
}
The convexMenu file is the following:
StyleProvider convexMenu(BuildContext context, Color activeColor,
List<nt.Notification>? notifications, Map<String, String> translation) {
var counter = 0;
if (notifications != null) {
for (var notification in notifications) {
if (!notification.read) {
counter++;
}
}
}
return StyleProvider(
style: Style(),
child: ConvexAppBar.badge(
{3: counter == 0 ? '' : counter.toString()},
badgeMargin: const EdgeInsets.only(bottom: 32, left: 34),
key: appBarKey,
style: TabStyle.fixedCircle,
backgroundColor: Colors.white,
color: Colors.grey,
activeColor: activeColor,
items: [
TabItem(
icon: const Icon(
ReceivedIcon.receivedIcon,
color: Colors.grey,
size: 12,
),
title: translation['receivedMenuText'] ?? '',
activeIcon: const Icon(
ReceivedIcon.receivedIcon,
color: Color(0xff00c9ff),
size: 12,
),
),
TabItem(
icon: const Icon(SentIcon.sentIcon, color: Colors.grey),
title: translation['sentMenuText'] ?? '',
activeIcon: const Icon(SentIcon.sentIcon, color: Color(0xffa98e47)),
),
const TabItem(
icon: Icon(
Icons.add_rounded,
size: 48,
color: Colors.white,
),
),
TabItem(
icon: Icons.notifications,
// title: AppLocalizations.of(context)!.notifications
title: translation['notificationsMenuText'] ?? '',
),
TabItem(
icon: Icons.menu,
// title: AppLocalizations.of(context)!.menu
title: translation['menuText'] ?? '',
),
],
// initialActiveIndex: 1,
onTap: (int i) {
context.read<HomePageCubit>().changeTab(context, i);
},
),
);
}
class Style extends StyleHook {
#override
double get activeIconSize => 20;
#override
double get activeIconMargin => 10;
#override
double get iconSize => 20;
#override
TextStyle textStyle(Color color, String? s) {
return TextStyle(fontSize: 12, color: color);
}
}
Is there a way to make ConvexMenuBar work on the web? It worked perfectly on the mobile version.

Related

Update an item on a list in my flutter app without having same item with updated value

When I run the code, the list adds another item instead of updating the old item (still displaying the old item) which the index was found. I have also tried as using keys on my listView.builder() listView.custom(), and on the Custom Widget I created which renders on the listView.builder() all give same result. Is there something I'm not doing right?
using the indexWhere() this way,
void updateProduct(String id, ProductSaver productSaver) {
final prodIndex = _productList.indexWhere((element) => element.id == id);
final newProduct = ProductSaver(
title: productSaver.title,
description: productSaver.description,
imageUrl: productSaver.imageUrl,
price: productSaver.price);
_productList[prodIndex] = newProduct as Product;
notifyListeners();
}
and this way:
void updateProduct(String id, Product newItem){
final pIndex = _productList.indexWhere((element)=>element.id == id);
if(pIndex >= 0){
_productList[pIndex] = newItem;}
notifyListeners();}
I also used the list.contain()
void updateProduct({String? id, required Product newItem}) {
final itemIndex = _productList.indexWhere((prod) => prod.id! == id);
if (_productList.contains(_productList[itemIndex])) {
_productList[itemIndex] = newItem;
}
notifyListeners();
}
Here is the build() of the code:
#override
Widget build(BuildContext context) {
final providerData = Provider.of<Products>(context);
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(EditProductScreen.routeName);
},
icon: const Icon(
Icons.add,
size: 30,
),
)
],
backgroundColor: MyColor.primaryColor,
),
body: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
itemCount: providerData.item.length,
itemBuilder: (context, index) {
return Column(
children: [
UserProductItem(
id: providerData.item[index].id!,
imageUrl: providerData.item[index].imageUrl,
title: providerData.item[index].title),
)
],
);
}),
);
}
}
the above UserProductItem() Widget.
class UserProductItem extends StatelessWidget {
const UserProductItem({
Key? key,
required this.imageUrl,
required this.title,
required this.id,
}) : super(key: key);
final String imageUrl;
final String title;
final String? id;
#override
Widget build(BuildContext context) {
final productData = Provider.of<Products>(context);
return ListTile(
key: UniqueKey(),
leading: Image.network(
imageUrl,
),
title: Text(
title,
style: const TextStyle(
color: MyColor.primaryColor,
fontSize: 17,
),
),
trailing: FittedBox(
child: Row(
children: [
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.green,
),
onPressed: () {
Navigator.of(context)
.pushNamed(EditProductScreen.routeName, arguments: id!);
},
),
IconButton(
icon: const Icon(
Icons.delete_outline_outlined,
color: Colors.red,
),
onPressed: () {
productData.removeItem(id!);
},
),
],
),
),
);
}
}
You changed the value at a specific index. Instead you can try adding an item to the list
if (_productList.contains(_productList[itemIndex])) {
_productList.add(newItem);//<--here
}

How to customize the menu in Flutter?

I'm developing a dynamic menu in Getx and I'm iterating through a list. It wouldn't be a problem if all the menus were the same, the problem is that the Editores menu is not the default as you can see in the image. Is there a way to leverage my code and print the Editores menu to the screen?
Controller
class HomeController extends GetxController {
final HomeRepository repository;
HomeController(this.repository);
final searchDrawerEC = TextEditingController();
//Variáveis para compor o menu
final _selectedIndex = 0.obs;
int get selectedIndex => _selectedIndex.value;
set selectedIndex(int newValue) => _selectedIndex(newValue);
final items = [].obs;
#override
void onInit() {
items.add(
{
'titulo': 'Home',
'icone': const Icon(
Icons.house_rounded,
color: Colors.white,
),
'rota': Routes.home,
},
);
items.add(
{
'titulo': 'Novas Edições',
'icone': Image.asset(AppImages.novasEdicoes),
'rota': Routes.newEditions,
},
);
items.add(
{
'titulo': 'Editores',
'icone': Image.asset(AppImages.editores),
'rota': '/',
},
);
items.add(
{
'titulo': 'Seguindo',
'icone': Image.asset(AppImages.seguinte),
'rota': '/',
},
);
items.add(
{
'titulo': 'Favoritos',
'icone': Image.asset(AppImages.favorite),
'rota': Routes.favoriteds,
},
);
items.add(
{
'titulo': 'Net',
'icone': Image.asset(AppImages.net1),
'rota': '/',
},
);
items.add(
{
'titulo': 'Configurações',
'icone': const Icon(
Icons.settings,
color: Colors.white,
),
'rota': '/',
},
);
items.add(
{
'titulo': 'MMN',
'icone': const Icon(
Icons.person,
color: Colors.white,
),
'rota': '/',
},
);
super.onInit();
}
Page
class DrawerContentWidget extends GetView<HomeController> {
final int? editores;
final bool? showEditores;
// ignore: use_key_in_widget_constructors
const DrawerContentWidget({this.editores, this.showEditores});
#override
Widget build(BuildContext context) {
return Column(
children: [
CardInfoWidget(
showNet: false,
showEdit: true,
color: const Color(0XFF005E6C),
editor: Get.find<AppController>().currentUser,
),
Column(
children: [
Obx(() => SizedBox(
height: 200,
child: Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: controller.items.length,
itemBuilder: (_, index) {
final _item = controller.items[index];
return Obx(() => Container(
decoration: (controller.selectedIndex == index)
? const BoxDecoration(
border: Border(
top: BorderSide(width: 3.0, color: Colors.white),
bottom: BorderSide(width: 3.0, color: Colors.white),
),
)
: null,
child: Card(
color: const Color(0XFF007E94),
elevation: 3,
child: ListTile(
title: Text(
_item['titulo'],
style: const TextStyle(color: Colors.white),
),
leading: _item['icone'],
selected: (controller.selectedIndex == index),
onTap: () => Get.toNamed(_item['rota']),
),
),
),
);
},
),
],
),
)
),
],
)
],
);
}
}
Solved, envolve the card in some OBX then use the controller.selectedIndex = index with the route like this.
onTap: () {
controller.selectedIndex = index ;
Get.toNamed(_item['rota']);
}

how to store tasks in temporary storage in flutter (use Shared preferences or something similar)

I created a program to create tasks, everything works as it should, I made such options as validation, deleting the task, changing the theme. But the problem is that when I restart the app all the tasks are deleted. I want to keep them in storage, temporarily or permanently. And I can't realize it. Someone may have experience as such to implement ?? or examples of similar problems. My goal is that after restarting the application, all tasks remain.
My code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main () {
runApp(MaterialApp(
home: App(),
));
}
class ListItem{
String todoText;
bool todoCheck;
ListItem(this.todoText, this.todoCheck);
}
class _strikeThrough extends StatelessWidget{
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget(){
if(todoCheck){
return Text(
todoText,
style: TextStyle(
fontSize: 22.0,
),
);
}
else{
return Text(
todoText,
style: TextStyle(
fontSize: 22.0
),
);
}
}
#override
Widget build(BuildContext context){
return _widget();
}
}
class App extends StatefulWidget{
#override
AppState createState(){
return AppState();
}
}
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
class AppState extends State<App> {
bool valText = true;
var counter = 0;
var IconsType = Icons.wb_sunny ;
late Color ColorType = Colors.black;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<ListItem> WidgetList = [];
#override
void dispose() {
textController.dispose();
popUpTextController.dispose();
super.dispose();
}
#override
void initState() {
addToSP(defaultList).then((_) => getSP());
super.initState();
}
Future<void> addToSP(List<List<ListItem>> tList) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('graphLists', jsonEncode(tList));
}
void getSP() async {
final prefs = await SharedPreferences.getInstance();
final List<dynamic> jsonData =
jsonDecode(prefs.getString('graphLists') ?? '[]');
WidgetList = jsonData.map<List<ListItem>>((jsonList) {
return jsonList.map<TodoInfo>((jsonItem) {
return ListItem.fromJson(jsonItem);
}).toList();
}).toList();
setState(() {});
}
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
appBar: AppBar(
title: Text("Список задач"),
actions: <Widget>[
IconButton(
icon: Icon(IconsType,color : ColorType
),
onPressed:() =>
{
if (_notifier.value == ThemeMode.light) {
_notifier.value = ThemeMode.dark,
IconsType = Icons.dark_mode,
ColorType = Colors.white,
} else
{
_notifier.value = ThemeMode.light,
IconsType = Icons.wb_sunny,
ColorType = Colors.black,
}
}
)
],
//backgroundColor: Colors.orange[500],
iconTheme: IconThemeData(
color: Colors.white
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: () {
if (textController.text.replaceAll(" ", "").isNotEmpty) {
WidgetList.insert(0, new ListItem(textController.text.replaceAll(" ", ""), false));
setState(() {
valText = true;
textController.clear();
});
}
else
{
setState(() {
valText = false;
});
}
},
)
],
),
),
Container(
width: MediaQuery
.of(context)
.size
.height * 0.45,
child: TextField(
style: TextStyle(
fontSize: 22.0,
//color: Theme.of(context).accentColor,
),
controller: textController,
cursorWidth: 5.0,
autocorrect: true,
autofocus: true,
//onSubmitted: ,
),
),
Align(
child:
(valText == false) ?
Align(child: Text(("Задача пустая"),
style: TextStyle(
fontSize: 25.0, color: Colors.red)),
alignment: Alignment.center) :
Align(child: Text((""),),
alignment: Alignment.center)),
Expanded(
child: ReorderableListView(
children: <Widget>[
for(final widget in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
//key: ValueKey("Checkboxtile $widget"),
value: widget.todoCheck,
title: _strikeThrough(
widget.todoText, widget.todoCheck),
onChanged: (checkValue) {
//_strikethrough toggle
setState(() {
if (!checkValue!) {
widget.todoCheck = false;
}
else {
widget.todoCheck = true;
}
});
},
),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.redAccent,
),
direction: DismissDirection.endToStart,
movementDuration: const Duration(
milliseconds: 200),
onDismissed: (dismissDirection) { //Delete Todo
WidgetList.remove(widget);
},
),
)
],
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},
),
)
],
),
)
);
}
);
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({
required this.todoText,
required this.todoCheck,
});
factory TodoInfo.fromJson(Map<String, dynamic> json) {
return TodoInfo(
todoText: json["todoText"],
todoCheck: json["todoCheck"]);
}
factory TodoInfo.fromMap(Map<String, dynamic> map) => TodoInfo(
todoText: map["todoText"]?? '',
todoCheck: map["todoCheck"] ?? '',
);
Map<String, dynamic> toJson() {
return {
"todoText": todoText,
"todoCheck": todoCheck
};
}
#override
String toString() => '{todoText: $todoText, todoCheck: $todoCheck}';
}
My skrin
Here are some options for persistent memory.
You could use any of the storage functions/plugins available for flutter as per your need or interest.
Some of them are
Seflite
Shared preferences
Hive
Example on how to use Shared Preferences: LINK

Flutter Slidable SlidableAction Calling onPressed even when it has not been pressed

I am using a library called flutter_slidable . Below is my fetchItems method
static Future<List<Item>> fetchItems(String url) async {
try {
// pub spec yaml http:
// import 'package:http/http.dart' as http;
final response = await http.get(
Uri.parse(
url),
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer tltsp6dmnbif01jy9xfo9ssn4620u89xhuwcm5t3",
}) /*.timeout(const Duration(seconds: Config.responseTimeOutInSeconds))*/;
final List<Item> itemsList;
if (response.statusCode == 200) {
itemsList = json
.decode(response.body)
// In event of failure return line below
//.cast<Map<String, dynamic>>()
.map<Item>((json) => Item.fromJson(json))
.toList();
} else if (response.statusCode == 401) {
itemsList = [];
} else {
itemsList = [];
}
return itemsList;
} catch (e) {
if (kDebugMode) {
Logger().wtf(
"FetchItemsException $e \n\nResponseStatusCode ${statusCode!}");
}
rethrow;
}
}
And below is the code for my page that i populate
class ClassListWithSearchOnAppBarCustomCard extends StatefulWidget {
const ClassListWithSearchOnAppBarCustomCard({Key? key}) : super(key: key);
#override
_ClassListWithSearchOnAppBarCustomCardState createState() =>
_ClassListWithSearchOnAppBarCustomCardState();
}
class _ClassListWithSearchOnAppBarCustomCardState
extends State<ClassListWithSearchOnAppBarCustomCard> {
List<Item>? itemsList;
Future populateItemsList() async {
final itemsList = await AsyncFutures.fetchItems(
"https://api.json-generator.com/templates/ueOoUwh5r44G/data");
setState(() {
this.itemsList = itemsList;
});
}
#override
void initState() {
super.initState();
populateItemsList();
}
onSearch(String searchValue) {}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey.shade900,
leading: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(
Icons.arrow_back,
color: Colors.white,
)),
title: Container(
child: TextField(
onChanged: (value) => onSearch(value),
cursorHeight: 21.0,
decoration: InputDecoration(
filled: true,
fillColor: Colors.grey[850],
contentPadding: EdgeInsets.all(0),
prefix: Icon(
Icons.search,
color: Colors.grey.shade500,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50),
borderSide: BorderSide.none),
hintStyle:
TextStyle(fontSize: 15, color: Colors.grey.shade500),
hintText: "Search"),
style: TextStyle(
color: Colors.white,
),
),
),
),
body: Column(children: [
Expanded(
child: Builder(
builder: (BuildContext context) {
if (itemsList == null) {
return iconProgressIndicator();
} else {
return RefreshIndicator(
// background color
backgroundColor: Colors.white,
// refresh circular progress indicator color
color: Colors.green,
onRefresh: () async {
setState(() {
populateItemsList();
});
},
child: ListView.builder(
itemCount: itemsList!.length,
itemBuilder: (BuildContext context, int index) {
// flutter_slidable: ^1.2.0
// import 'package:flutter_slidable/flutter_slidable.dart';
return Slidable(
// Specify whether the slider is dismissible
key: const ValueKey(1),
// Sliding from left to right
startActionPane: ActionPane(
// Types of Motion
// Behind Motion, Drawer Motion, Scroll Motion , Stretch Motion
motion: const DrawerMotion(),
// dismissible: DismissiblePane(onDismissed: () {
// onDismissedRemoveItem(
// itemsList![index].id ?? "");
// }),
children: [
// Start this side with delete action if you have already implemented dismissible
// If Start with other slidable action create a new method for the slidable with a build context
SlidableAction(
onPressed: deleteSlidableAction(
context, itemsList![index].id ?? ""),
backgroundColor: Colors.red.shade500,
foregroundColor: Colors.white,
icon: Icons.delete,
label: 'Delete',
),
SlidableAction(
onPressed: dialogSlidableAction(
context, itemsList![index].id ?? ""),
backgroundColor: Colors.blueAccent.shade400,
foregroundColor: Colors.white,
icon: Icons.check_box_outline_blank,
label: 'Dialog',
),
],
),
child: myCustomCardWidget(
itemsList![index].id ?? "",
itemsList![index].title ?? "",
itemsList![index].subTitle ?? '',
itemsList![index].imageUrl ??
Config.nullNetworkImage),
);
},
));
}
},
),
)
]));
}
deleteSlidableAction(BuildContext context, String? itemId) {
setState(() {
itemsList!.removeWhere((item) => item.id == itemId);
});
}
dialogSlidableAction(BuildContext context, String? itemId) {
print(itemId);
}
void onDismissedRemoveItem(String itemId) {
setState(() {
itemsList!.removeWhere((item) => item.id == itemId);
});
}
}
The problem i am having is that onPressed of SlidableAction for both Delete and Dialog are being called even before they are pressed and the populated list items are all removed
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
_yesPost(forMeList[i]["postID"]);
},
backgroundColor: Colors.green,
foregroundColor: Colors.white,
icon: Icons.check_circle_outline,
label: 'Yes',
),
try to add BuildContext.
that worked for me.

Scaffold in flutter

I am new to flutter. I have a question about scaffold in my project.
I have a home screen that I use to display the BottomNavigation widget. I guess that I also use if as a container to display all of the other pages/screens in so that the BottomNavigation will stay visible. Here is the code below:
class Home_Screen extends StatefulWidget {
static const String id = 'home_screen';
#override
_Home_ScreenState createState() => _Home_ScreenState();
}
// ignore: camel_case_types
class _Home_ScreenState extends State<Home_Screen> {
PageController _pageController = PageController();
List<Widget> _screens = [
AgentDashboardScreen(),
TransactionDetailScreen(),
AgentProfileScreen(),
];
int _selectedIndex = 0;
void _onPageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
void _itemTapped(int selectedIndex) {
if (selectedIndex == 3) {
Navigator.of(context).pushAndRemoveUntil(
// the new route
MaterialPageRoute(
builder: (BuildContext context) => WelcomeScreen(),
),
(Route route) => false,
);
} else {
_pageController.jumpToPage(selectedIndex);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
children: _screens,
onPageChanged: _onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _itemTapped,
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.home,
color: _selectedIndex == 0 ? Colors.blueAccent : Colors.grey,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.account_balance,
color: _selectedIndex == 1 ? Colors.blueAccent : Colors.grey,
),
label: 'Add Tran',
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
color: _selectedIndex == 2 ? Colors.blueAccent : Colors.grey,
),
label: 'Profile',
),
BottomNavigationBarItem(
icon: Icon(
Icons.album_outlined,
color: _selectedIndex == 3 ? Colors.blueAccent : Colors.grey,
),
label: 'Logout',
),
],
),
);
}
}
In one of the screens that I can navigate to from the BottomNavigator I am having issues with a large white space above the keyboard. I have read that having a scaffold inside another scaffold can cause this.
So, when I navigate to the next page do I have a scaffold inside another scaffold? Here is a snippet from the second page.
class TransactionDetailScreen extends StatefulWidget {
static const String id = 'transaction_detail_screen';
final QueryDocumentSnapshot trxns;
//final Trxns trxns;
//final QuerySnapshot queryTrxns = trxns;
TransactionDetailScreen([this.trxns]);
#override
_TransactionDetailScreenState createState() =>
_TransactionDetailScreenState();
}
class _TransactionDetailScreenState extends State<TransactionDetailScreen> {
String _trxnStatus = 'Listed';
#override
Widget build(BuildContext context) {
// Get the stream of transactions created in main.dart
final trxnProvider = Provider.of<TrxnProvider>(context);
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/Appbar_logo.png',
fit: BoxFit.cover, height: 56),
],
),
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
reverse: true,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text(
'Transaction Details',
style: TextStyle(
fontSize: 30,
),
),
SizedBox(
height: 8.0,
),
TextField(
autofocus: true,
keyboardType: TextInputType.text,
controller: clientFNameController,
textAlign: TextAlign.center,
onChanged: (value) {
trxnProvider.changeclientFName(value);
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Client First Name',
labelText: 'Client First Name'),
),
RoundedButton(
title: 'Save',
colour: Colors.blueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
trxnProvider.saveTrxn();
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => AgentDashboardScreen(),
),
);
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
),
SizedBox(
height: 8.0,
),
(widget != null)
? RoundedButton(
title: 'Delete',
colour: Colors.red,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
trxnProvider.deleteTrxn(widget.trxns['trxnId)']);
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => AgentDashboardScreen(),
),
);
setState(() {
showSpinner = false;
});
} catch (e) {
// todo: add better error handling
print(e);
}
},
)
: Container(),
],
),
),
),
);
}
}
The keyboard works/looks as expected (no white space above) if the textboxes are empty. Am I doing this correctly or should I do it differently?
Thanks