Thank you for checking :)
I don't know why the bottomnavigationbar's area filled half on screen. The bottomnavigationbar's area is filled black. I want the bottomnavigationbar to show for navigation only bottom area.
If I delete Expanded, the renderflex error is showing me..
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int visit = 0;
double height = 30;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
bottomNavigationBar: BottomBarFloating(
iconSize: 25,
items: items,
backgroundColor: Colors.white,
color: Colors.black,
colorSelected: PRIMARY_COLOR,
indexSelected: visit,
paddingVertical: 30,
onTap: (int index) => setState(() {
visit = index;
}),
),
);
}
}
class SearchOffScreen extends StatelessWidget {
const SearchOffScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return DefaultLayout(
child: SafeArea(
top: true,
bottom: false,
child: Column(
children: [
const SizedBox(height: 250.0,),
_SearchBox(),
Expanded(child: MyHomePage(title: '')),
],
),
),
);
}
}
class DefaultLayout extends StatelessWidget {
final Widget child;
final String? title;
const DefaultLayout({
required this.child,
this.title,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xffffffff),
body: child,
);
}
If I delete Expanded, the renderflex error is showing me..
How can I fix it?
Try this instead, use only one scaffold in a page
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int visit = 0;
double height = 30;
#override
Widget build(BuildContext context) {
return Container(
child: //your code here
);
}
}
class SearchOffScreen extends StatelessWidget {
const SearchOffScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return DefaultLayout(
child: SafeArea(
top: true,
bottom: false,
child: Column(
children: [
const SizedBox(height: 250.0,),
_SearchBox(),
Expanded(child: MyHomePage(title: '')),
],
),
),
);
}
}
class DefaultLayout extends StatelessWidget {
final Widget child;
final String? title;
const DefaultLayout({
required this.child,
this.title,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xffffffff),
body: child,
bottomNavigationBar: BottomBarFloating(
iconSize: 25,
items: items,
backgroundColor: Colors.white,
color: Colors.black,
colorSelected: PRIMARY_COLOR,
indexSelected: visit,
paddingVertical: 30,
onTap: (int index) => setState(() {
visit = index;
}),
),
);
}
Related
Question: I want to call openDrawer from onPressed() of Header.dart
The Drawer.dart, Header.dart and Home.dart files are separate.
I have triedScaffold.of(context).openDrawer();
not worked.
I tried using cotroller but it not work.
Perhaps it was not used in the right way.
I would appreciate any advice you could give me.
code:
Codes are omitted.
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: Header('App'),
endDrawer: Menu(),
)}}
class Header extends StatefulWidget implements PreferredSizeWidget {
const Header(this.heading, {Key? key});
final String heading;
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
#override
State<Header> createState() => _HeaderState(heading);
}
class _HeaderState extends State<Header> {
_HeaderState(this.heading);
final String heading;
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
// _key.currentState!.openDrawer();
},
),]
);
}
}
class Menu extends StatelessWidget {
const Menu({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer()
}
You're using an end drawer, so instead of Scaffold.of(context).openDrawer(), you should use Scaffold.of(context).openEndDrawer():
class _HeaderState extends State<Header> {
_HeaderState(this.heading);
final String heading;
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
),]
);
}
}
Side Note: You don't need to pass arguments from the StatefulWidget to the State - Every instance of State has a widget property, which you can use to access the properties of the parent widget.
In your case it would look like:
class Header extends StatefulWidget implements PreferredSizeWidget {
const Header(this.heading, {Key? key});
final String heading;
#override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
#override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
#override
Widget build(BuildContext context) {
return AppBar(
centerTitle: false,
// Assuming this is what the heading argument is for
title: Text(widget.heading),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu, size: 30.0),
tooltip: '',
onPressed: () {
Scaffold.of(context).openEndDrawer();
},
),
],
);
}
}
I have a list of integers. Each of this item is displayed in a statefull widget by iterating the list in the build method.
import 'package:flutter/material.dart';
import 'package:widget_list/ItemWidget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Item list state demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Item list state demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static int itemsCount = 0;
final List<int> _items = List.empty(growable: true);
void _add() {
setState(() {
_items.add(itemsCount++);
});
}
void _remove() {
setState(() {
_items.removeAt(0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: [
TextButton(
onPressed: () => _add(),
child: const Text('Add item'),
),
TextButton(
onPressed: () => _items.isNotEmpty ? _remove() : null,
child: const Text('Remove item'),
),
],
),
for (var item in _items) ItemWidget(item: item),
],
),
),
);
}
}
Each of this widget, has a statically incremented integer "id" in it's state. Both the item and the widget id are displayed.
import 'package:flutter/material.dart';
var widgetCount = 0;
class ItemWidget extends StatefulWidget {
final int item;
const ItemWidget({
required this.item,
Key? key,
}) : super(key: key);
#override
State<ItemWidget> createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
final int widgetId = widgetCount++;
#override
Widget build(BuildContext context) {
print("Item ${widget.item} / Widget $widgetId");
return Text("Item ${widget.item} / Widget $widgetId");
}
}
When I add an item in the list, it is displayed in a newly generated widget. E.g. first item 0 is displayed in widget 0.
But if I remove an item at the beginning of the list (e.g. item 0), it's not the first widget that is destoyed, but the last one. The item 1 is then displayed in widget 0.
The widget item is final, so it cannot change. The widget ids are still the same, so the states were not rebuild. Then, why are the states no more consistent with the widgets?
This is done in FLutter desktop for Linux, v3.0.1
In the itemWidget you are creating a value from 0 so for each element that is rendered it will start from 0. please check the code below
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Item list state demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Item list state demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static int itemsCount = 0;
final List<ItemInfo> _items = List.empty(growable: true);
void _add() {
setState(() {
itemsCount++;
_items.add(ItemInfo(itemsCount, itemsCount));
});
}
void _remove() {
setState(() {
_items.removeAt(0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: [
TextButton(
onPressed: () => _add(),
child: const Text('Add item'),
),
TextButton(
onPressed: () => _items.isNotEmpty ? _remove() : null,
child: const Text('Remove item'),
),
],
),
for (var item in _items) ItemWidget(item: item),
],
),
),
);
}
}
and Itemwidget to be like this
class ItemWidget extends StatefulWidget {
final ItemInfo item;
const ItemWidget({
required this.item,
Key? key,
}) : super(key: key);
#override
State<ItemWidget> createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
#override
Widget build(BuildContext context) {
return Text(
"Item ${widget.item.itemVal} / Widget ${widget.item.itemIndex}");
}
}
also I created a class named ItemInfo which will hold both the value and its index.
class ItemInfo {
int itemVal;
int itemIndex;
ItemInfo(this.itemVal, this.itemIndex);
}
I would like to have a persistent container occupy the space about my material Scaffolds AppBar. I would like the Scaffold to resize to take up the available space.
When I try to do this, my Scaffold continues to be the height of the entire screen, and it is simply pushed lower, with a portion overflowing off the screen.
Is there a way I can have the Scaffold to resize to the available space?
Here is what I have coded so far...
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return PersistenTopBar(
child: Scaffold(
appBar: AppBar(
title: const Text("Test App"),
),
body: Container(),
),
);
}
}
class PersistenTopBar extends StatelessWidget {
final Widget child;
const PersistenTopBar({Key? key , required this.child }) : super(key: key);
#override
Widget build(BuildContext context) {
var mediaQuery = MediaQuery.of(context);
return Column(
children: [
Container(
width: double.infinity,
height: 200,
color: Colors.red,
),
SizedBox(
width: mediaQuery.size.width,
height: mediaQuery.size.height,
child: child,
),
],
);
}
}
You could also create a CustomAppBar that would take as children a topChild and an appBar.
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double height;
final Widget topChild;
final AppBar appBar;
const CustomAppBar(
{Key? key,
this.height = 200.0,
required this.topChild,
required this.appBar})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(child: topChild),
appBar,
],
);
}
#override
Size get preferredSize => Size.fromHeight(height);
}
Full code sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
topChild: Container(color: Colors.red),
appBar: AppBar(title: const Text('My awesome Test App')),
),
body: const Center(
child: Text(
"Test App",
style: TextStyle(fontSize: 32.0),
),
),
);
}
}
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double height;
final Widget topChild;
final AppBar appBar;
const CustomAppBar(
{Key? key,
this.height = 200.0,
required this.topChild,
required this.appBar})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(child: topChild),
appBar,
],
);
}
#override
Size get preferredSize => Size.fromHeight(height);
}
the available space = mediaQuery.size.height - the Height of the Container above the appBar so the SizedBox under the appBar wil be :
SizedBox(
width: mediaQuery.size.width,
height: mediaQuery.size.height - 200,
child: child,
),
the result:
or you can wrap your SizedBox with Expanded Widget :
Expanded(
child: SizedBox(
width: mediaQuery.size.width,
child: child,
),
),
the same result :
I have this Flutter code sample.
class HomePage extends StatefulWidget {
final String title;
const HomePage({Key? key, required this.title}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(flex: 1, child: SideActivityBar()),
// The wiget Text('Initial') does not show up!
Expanded(flex: 4, child: MainPanel(color: Colors.blue.shade100, page: Text('Initial'))),
],
);
}
}
class MainPanel extends StatefulWidget {
final Widget page;
final Color color;
const MainPanel({Key? key, required this.color, required this.page}) : super(key: key);
#override
State<MainPanel> createState() => _MainPanelState();
}
class _MainPanelState extends State<MainPanel> {
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: widget.color,
child: widget.page,
);
}
}
The widget Text('Initial') does not show up though I can see MainPanel showing up. What am I missing or doing wrong?
How to link two pageviews in flutter?
i.e. if one of them goes to page x the other should go to page x as well.
I thought two PageViews having the same controller would do the trick.
But that doesn't seem to be the case.
I tried having a list of controllers and when one of the pageviews' page changes, I'm calling jumpToPage on all the other pageviews' controllers but all the other PageViews are not in the widget runtime tree initially (They're outside the screen) thus giving out errors.
In my case PageView(children:[Pageview(...), Pageview(...)]) is the structure.
And after I open the other pageviews once, the errors are all gone but the current pageview is also getting jumped even though I removed it.
There're no infinite loops because of the other pageview's event firing at the same time.
/// Inside a stateful widget
PageView(
controller: widget.controller,
onPageChanged: (pno) {
widget.controllers.where((x) {
return x != widget.controllers[widget.idx];
}).forEach((colpv) {
colpv.controller?.jumpToPage(pno);
});
},
);
This is a minimal example that reproduces what I'm doing. It's in the ColPageView widget.
The full code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Experiments',
theme: ThemeData.dark(),
home: MyHomePage(title: 'FlutterExps'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<PageControllerC> _controllers;
PageController _rowController;
PageController _mainController;
#override
void initState() {
_controllers = [
PageControllerC(
controller: PageController(keepPage: true),
recorded: 0,
),
PageControllerC(
controller: PageController(keepPage: true),
recorded: 1,
),
];
_controllers.forEach((f) {
f.controller.addListener(() {
print("Listener on ${f.recorded}");
});
});
_mainController = PageController();
_rowController = PageController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _rowController,
children: [
ColPageView(
idx: 0,
controllers: _controllers,
controller: _mainController,
children: <Widget>[
ColoredWidget(
color: Colors.cyan,
direction: ">",
),
ColoredWidget(
color: Colors.orange,
direction: ">>",
),
],
),
ColPageView(
idx: 1,
controllers: _controllers,
controller: _mainController,
children: [
ColoredWidget(
color: Colors.green,
direction: "<",
),
ColoredWidget(
color: Colors.yellow,
direction: "<<",
),
],
),
],
),
);
}
}
class PageControllerC {
PageController controller;
int recorded;
PageControllerC({
this.recorded,
this.controller,
});
}
class ColPageView extends StatefulWidget {
final List<Widget> children;
final List<PageControllerC> controllers;
final int idx;
final PageController controller;
const ColPageView({
Key key,
this.children = const <Widget>[],
#required this.controllers,
#required this.idx,
this.controller,
}) : super(key: key);
#override
_ColPageViewState createState() => _ColPageViewState();
}
class _ColPageViewState extends State<ColPageView> {
#override
Widget build(BuildContext context) {
return PageView(
controller: widget.controllers[widget.idx].controller,
// controller: widget.controller,
scrollDirection: Axis.vertical,
children: widget.children,
onPageChanged: (pno) {
widget.controllers.where((x) {
return x != widget.controllers[widget.idx];
}).forEach((colpv) {
// if (colpv != widget.controllers[widget.idx]) {
colpv.controller?.jumpToPage(pno);
// }
// else{
print("memmem ${widget.idx}");
// }
});
print("col-${widget.idx} changed to $pno");
},
);
}
}
class ColoredWidget extends StatefulWidget {
final Color color;
final String direction;
const ColoredWidget({
Key key,
#required this.color,
#required this.direction,
}) : super(key: key);
#override
_ColoredWidgetState createState() => _ColoredWidgetState();
}
class _ColoredWidgetState extends State<ColoredWidget>
with AutomaticKeepAliveClientMixin<ColoredWidget> {
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
color: widget.color,
child: Center(
child: Text(
widget.direction,
style: TextStyle(
fontSize: 100,
color: Colors.black,
),
),
));
}
#override
bool get wantKeepAlive => true;
}
I was able to link two pageviews given that they both reside in a pageview.
Note: They're discretely linked.
Maintain a list of controllers
Track the current vertical position in the HomePage widget.
And also the current horizontal pageview's position.
If a widget's page is being changed and it is visible in the viewport then make all other pages jump to where this goes. Check if it is in the widget tree before making it jump.
Else if it's not in the viewport don't apply the same callback as it should only be affected by the one in the viewport (or the currently scrolling one).
When initializing any pageview check the current vertical position and jump to that page.
This is not efficient as I'm keeping all the pageviews in the widget tree alive even if they are not visible. (I will update the answer if I come up with one that is efficient)
This is working because both pageviews are in a single pageview which is horizontal.
I will try to provide another example where both the pageviews are in the viewport (in a row for example) and the linking is continuous.
This can be extended to multiple page views and which leads to a fullscreen GridView.
Full code.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Experiments',
theme: ThemeData.dark(),
home: MyHomePage(title: 'FlutterExps'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<PageController> _controllers;
PageController _rowController;
ValueNotifier<int> _horizPage = ValueNotifier(0);
ValueNotifier<int> _vertPage = ValueNotifier(0);
#override
void initState() {
_controllers = [
PageController(keepPage: true),
PageController(keepPage: true),
];
_rowController = PageController();
_horizPage.value = _rowController.initialPage;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _rowController,
onPageChanged: (pno) {
setState(() {
_horizPage.value = pno;
});
},
children: [
ColPageView(
idx: 0,
currHoriz: _horizPage,
vertstate: _vertPage,
controllers: _controllers,
children: <Widget>[
ColoredWidget(
color: Colors.cyan,
direction: ">",
),
ColoredWidget(
color: Colors.orange,
direction: ">>",
),
],
),
ColPageView(
idx: 1,
currHoriz: _horizPage,
vertstate: _vertPage,
controllers: _controllers,
children: [
ColoredWidget(
color: Colors.green,
direction: "<",
),
ColoredWidget(
color: Colors.yellow,
direction: "<<",
),
],
),
],
),
);
}
}
class ColPageView extends StatefulWidget {
final int idx;
final List<Widget> children;
final List<PageController> controllers;
final ValueNotifier<int> currHoriz;
final ValueNotifier<int> vertstate;
const ColPageView({
Key key,
this.children = const <Widget>[],
#required this.controllers,
#required this.currHoriz,
#required this.vertstate,
#required this.idx,
}) : super(key: key);
#override
_ColPageViewState createState() => _ColPageViewState();
}
class _ColPageViewState extends State<ColPageView> {
#override
void initState() {
widget.controllers[widget.idx] = PageController(
initialPage: widget.vertstate.value ?? 0,
keepPage: true,
);
super.initState();
}
#override
Widget build(BuildContext context) {
return PageView(
controller: widget.controllers[widget.idx],
scrollDirection: Axis.vertical,
children: widget.children,
onPageChanged: (widget.idx == widget.currHoriz.value)
? (pno) {
widget.controllers.forEach((colpv) {
if (colpv != widget.controllers[widget.idx]) {
if (colpv.hasClients && colpv.page != pno) {
colpv.jumpToPage(pno);
}
}
});
// Set latest vertical position
widget.vertstate.value = pno;
// print("col-${widget.idx} changed to $pno");
// set horizontal coord to be null
// As we've finished dealing with it
widget.currHoriz.value = null;
}
: null,
);
}
}
class ColoredWidget extends StatefulWidget {
final Color color;
final String direction;
const ColoredWidget({
Key key,
#required this.color,
#required this.direction,
}) : super(key: key);
#override
_ColoredWidgetState createState() => _ColoredWidgetState();
}
class _ColoredWidgetState extends State<ColoredWidget>
with AutomaticKeepAliveClientMixin<ColoredWidget> {
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
color: widget.color,
child: Center(
child: Text(
widget.direction,
style: TextStyle(
fontSize: 100,
color: Colors.black,
),
),
));
}
#override
bool get wantKeepAlive => true;
}