flutter, Why not render ListView<Widget> when build method called? - flutter

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Center(child: TestWidget())));
}
}
class TestWidget extends StatefulWidget {
TestWidgetState createState() => new TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
List<Widget> _bodyItems = [];
List<Widget> _topItems = [];
final Widget _boundary = Column(
children: <Widget>[
SizedBox(
height: 10,
),
SizedBox(
child: Container(
color: Colors.black12,
),
height: 1,
),
SizedBox(
height: 10,
),
],
);
#override
void initState() {
super.initState();
Widget e = GestureDetector(key: Key("0"), onTap: () {
onChangedFunction(Key("0"));
},child: Text("This text is on body range"));
_bodyItems.add(e);
_topItems = [];
}
void onChangedFunction(Key key) async {
setState(() {
_bodyItems.removeAt(0);
_topItems.add(Text("This text is on top range."));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
SizedBox(
height: 10,
),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 100,
),
child: ListView(
scrollDirection: Axis.horizontal,
children: _topItems,
),
),
_boundary,
ConstrainedBox(
constraints: BoxConstraints(
minHeight: 450,
maxHeight: 450,
),
child: Column(
children: _bodyItems,
),
),
_boundary,
],
)));
}
}
result
top items : [Text("This text is on top range.")]
This code deletes the body item when the widget of the body item is tapped and added item at the top item.
Looking at the result, you can see that the data disappeared from the body item widget and the data was added to the top item widget.
However, the data of the top item is not render.
I want to know what the reason is.

This happens because Children is constant, you can see that if you look into definition.
You will find following line in implementation of ListView.
List<Widget> children = const <Widget>[],
but you can change widget in side children, so you have to give list as a children of list.
As Foolowing.
children: [..._topItems],

Replace _topItems and _bodyItems with below
children: [..._topItems], & children: [..._bodyItems], --inside your body

Related

click on edit button container expand and show options

container where I want clicking in edit button container expand in height and show option when I click on edit button container will close
You need to set bool for expand or not in edit button.
You can follow the bellow code.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
bool isEdit;
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
#override
void initState() {
isEdit = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: RaisedButton(
onPressed: () {
setState(() {
isEdit = !isEdit;
});
},
child: Text("Edit"),
),
),
isEdit
? ConstrainedBox(
constraints: BoxConstraints(
maxWidth: double.infinity,
minHeight: 0.0,
maxHeight: 200.0,
),
child: Container(
height: 50,
width: 50,
color: Color(0xffff0000),
child: TextField(),
),
)
: Container()
],
),
),
),
);
}
}

Is it possible to create links to sections in the same page in flutter web?

I want to create a website using flutter web but I'm unable to navigate to sections in the same page. Here's an example of what I want to achieve using flutter.
P.S. Navigator is not working:
I created an example with PageView
class MyHomePage extends StatelessWidget {
var list = ["Home","Services", "Work", "About"];
var colors = [Colors.orange, Colors.blue, Colors.red, Colors.green];
PageController controller = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Row(
children: <Widget>[
Container(
width: 50,
height: 50,
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10)
),
),
Spacer(),
Row(
children: List.generate(3, (index){
return GestureDetector(
onTap: (){
_scrollToIndex(index);
},
child: Container(
margin: EdgeInsets.all(8),
child: Text(
list[index+1]
),
),
);
}),
)
],
),
Expanded(
child : PageView(
scrollDirection: Axis.vertical,
pageSnapping: false,
controller: controller,
children: List.generate(list.length, (index){
return Container(
width: MediaQuery.of(context).size.width,
height: double.maxFinite,
color: colors[index],
child: Center(
child: Text(
list[index],
style: TextStyle(
color: Colors.white,
fontSize: 50
),
),
),
);
})
),
),
],
)
),
);
}
void _scrollToIndex(int index) {
controller.animateToPage(index + 1, duration: Duration(seconds: 2), curve: Curves.fastLinearToSlowEaseIn);
}
}
The output:
ScrollController is the thing you are looking for.
Add a new one to your ScrolView and you can set where you want it to scroll to.
Josteve mentioned a way of doing it. But I'd like to show the other way which provides more features as one would expect in the gif example you have put.
You can see the demo here: https://mohith7548.github.io/portfolio/
My project has 3 sections called About, Blog & Projects. It also has another top section called Home. So the order of screens is Home, About, Blog & Projects. Each section takes full-screen height & width. So the starting offset for these pages are [0 * screenHeight, 1 * screenHeight, 2 * screenHeight, 3 * screenHeight] respectively. screenHeight can be accessed by MediaQuery.of(context).size.height inside build method.
class Portfolio extends StatefulWidget {
#override
_PortfolioState createState() => _PortfolioState();
}
class _PortfolioState extends State<Portfolio> {
ScrollController _scrollController;
String _curNavItem;
static double offsetHome = 0;
static double offsetAbout = SizeConfig.screenHeight;
static double offsetBlog = 2 * SizeConfig.screenHeight;
static double offsetProjects = 3 * SizeConfig.screenHeight;
#override
void initState() {
super.initState();
_scrollController = ScrollController();
}
#override
void dispose() {
super.dispose();
_scrollController.dispose();
}
void scrollTo(String title) {
double offset = 0;
switch (title) {
case Constants.HOME:
offset = offsetHome;
break;
case Constants.ABOUT:
offset = offsetAbout;
break;
case Constants.BLOG:
offset = offsetBlog;
break;
case Constants.PROJECTS:
offset = offsetProjects;
break;
}
setState(() {
_curNavItem = title;
});
// animate to the pag
_scrollController.animateTo(
offset,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOutQuart,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
physics: PageScrollPhysics(), // use NeverScrollableScrollPhysics() to block user scrolling
controller: _scrollController,
slivers: <Widget>[
// This is just SliverAppBar wrapped in InterheritedWidget called NavState
// You can use just SliverAppBar
NavState(
curNavItem: _curNavItem,
scrollTo: scrollTo,
child: AppBanner(key: _appBannerKey), // SliverAppBar in another file
),
SliverList(
delegate: SliverChildListDelegate([
About(),
Blog(),
Projects(),
]),
)
],
),
);
}
}
You can do this in different ways:
TabBarView https://stackoverflow.com/a/60624536/10976088
PageView https://stackoverflow.com/a/60778791/10976088
NavigationRail https://api.flutter.dev/flutter/material/NavigationRail-class.html
My method: Using a state management way to keep name or index of content pages and change visible page. I do it with the Riverpod package here:
Suppose you want to have a fixed SidebarView and HeaderView in all pages and also you have a ContentPage that will be changed.
So you can have a RootPage including these 3 sections and change ContentPage by the riverpod, so that only ContentPage will be changed.
class RootPage extends StatelessWidget {
const RootPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: SidebarView(),
body: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (Responsive.isDesktop(context))
const Expanded(
flex: 1,
child: SidebarView(),
),
Expanded(
flex: 5,
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
HeaderView(),
Expanded(
child: Padding(
padding: const EdgeInsets.all(16),
child: Consumer(
builder: (context, ref, _) {
var watch = ref.watch(pageVisibleStateProvider);
return contentPageSelection(watch.state);
},
),
),
),
],
),
),
),
],
),
);
}
}
simply change content page:
Widget contentPageSelection(String pageName){
switch(pageName){
case "page1":
return Page1();
case "page2":
return Page2();
case "page3":
return Page3();
default:
return DefaultPage();
}
}
where:
final pageVisibleStateProvider = StateProvider<String>((_) => "defaultPage");
and:
class SidebarView extends StatelessWidget {
const SidebarView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Text("sidebar content"),
);
}
}
class HeaderView extends StatelessWidget {
const HeaderView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Text("HeaderView content"),
);
}
}
Now you can change content page. for example you want to show Page2:
ElevatedButton(
onPressed: (){
ref.read(pageVisibleStateProvider.notifier).state = "page2";
},
child: Text("go to page 2"),
)
where page2 and other content pages only includes content not sidebar or header:
class Page2 extends StatelessWidget {
const Page2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text("page2 content");
}
}

Need help in making a reusable widget in flutter

So I am making an app with flutter. So in the main.dart file i am making a component which is basically a bunch of widgets wrapped together. I have to use this component multiple times so I thought of making these reusable component in another dart file and then importing it in main.dart.
This is my code for reusable.dart
import 'package:flutter/material.dart';
double mainTab = 150;
class TileData extends StatefulWidget {
#override
_TileDataState createState() => _TileDataState();
}
class _TileDataState extends State<TileData> {
#override
Widget build(BuildContext context) {
return Container(
height: 200 - 15.0,
width: mainTab - 10.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 0, 0),
child: Column(
),
),
);
}
}
I plan to use this TileData Widget in my main.dart in this manner
ListView(
children: children: <Widget>[
TileData(
children: <Widget>[
Text('Element 1'),
]),
TileData(
children: <Widget>[
Text('Element 2'),
]),
TileData(
children: <Widget>[
Text('Element 3'),
],
)
],
),
So the children of the TileData() widget are actually the children of the column which was last wrapped in the widget in reusable.dart
Is there any way I can achieve this?
TileDate
import 'package:flutter/material.dart';
double mainTab = 150;
class TileData extends StatefulWidget {
List<Widget> widgetsList;
TileData({this.widgetsList});
#override
_TileDataState createState() => _TileDataState();
}
class _TileDataState extends State<TileData> {
#override
Widget build(BuildContext context) {
return Container(
height: 200 - 15.0,
width: mainTab - 10.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 15, 0, 0),
child: Column(
children: widget.widgetsList,
),
),
);
}
}
main
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/widgets/TileData.dart';
void main() => runApp(MaterialApp(
home: Scaffold(backgroundColor: Color(0xFF2d3447), body: MyApp()),
));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
TileData(
widgetsList: [Text("Element 1")],
),
TileData(
widgetsList: [Text("Element 2")],
),
TileData(
widgetsList: [Text("Element 3")],
)
],
);
}
}
In this way u can reuse
Create a property and use it as an argument in the constructor of the reusable widget.
final List<Widget> children;
TileData({this.children});
Then, in your build method, pass the property to the column.
Column(
children: widget.children
)
You can use ListView.builder()
add Constructor in your TileData widget
something like
ListView.builder(
itemCount:data.length,
itemBuilder: (context,index){
return TileData(data:"Element $index");
}
)

Is it possible to navigate through routes like in TabBar(with left slide) but without using TabBar but instead using buttons

I want to navigate from "Now Showing" to "Coming Soon" with a left swipe on the image, Moreover, I want the Appbar to not to move when I swipe, but I think it is only possible with tab bars and I am not sure, please give some advice if you know how to achieve this
enter image description here
As per GaboBrandX, he is correct. But you can also do one thing with the tabs also. The sliding will not work. It is complex, but you can give it a shot.
The picture I will give you, so there would be Tabs and below that there would be a container each container gets replaces by a click.
TabController controller;
int activeIndex = 0;
#override
void initState() {
super.initState();
this.tabController = TabController(length: 3, vsync: this);
}
//This changes the activeIndex based upon the tabController index
onTabChanged(){
this.setState((){
this.activeTabIndex = this.tabController.index;
});
}
//This will return your container, based upon your tabs selected
Widget getActiveTabView(){
case 1: {return YourSecondContainer();}
break;
default: {return YourFirstContainer();}
}
//Here is your full layout
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TMTabBar(titles: 'XYZ', controller: this.tabController, onChange: this.onTabChanged),
this.getActiveTabView(),
]
);
}
//Create a TabBarWidget and do this
class TMTabBar extends StatefulWidget {
String/List<String> titles;
TabController controller;
VoidCallback onChange;
TMTabBar({#required this.titles, #required this.controller, this.onChange});
#override
_TMTabBarState createState() => _TMTabBarState();
}
class _TMTabBarState extends State<TMTabBar> {
#override
void initState() {
//this is for changing the content as per the tabbar
this.widget.controller.addListener((){
if(this.widget.controller.indexIsChanging){
if(this.widget.onChange != null) this.widget.onChange();
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return TabBar(tab: YourTabs);
}
This basically gives you, what you're hoping for. Hope that helps. Thanks :)
Here I've made an example of what your looking for using a PageView. I've put only text on PageView's children, but you can put there your ListViews or anything you need. When tapping on a button the PageView navigates to the corresponding "page". This can be a starting point for you:
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
PageController _pageController = PageController(
initialPage: 0,
);
goToPage(num page) {
_pageController.animateToPage(
page,
duration: Duration(milliseconds: 350),
curve: Curves.easeIn,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
width: double.infinity,
height: 60.0,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: RaisedButton(
onPressed: () => goToPage(0),
child: Text('Now Showing'),
),
),
SizedBox(
width: 4.0,
),
Expanded(
child: RaisedButton(
onPressed: () => goToPage(1),
child: Text('Coming Soon'),
),
),
],
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: MediaQuery.of(context).size.height - 60.0,
child: PageView(
controller: _pageController,
children: <Widget>[
Center(
child: Text('Tab 1'),
),
Center(
child: Text('Tab 2'),
),
],
),
),
),
],
),
),
);
}
}

How Can I add onTap to image category

I want to add onTap to each category in my horizontal list view so how can I do it?
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Category(
image_location: "catogories/name.png",
),
],
),
);
}}
You can wrap your Category widget with GestureDetector widget.
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
//This will be called on tap
},
child:Category(
image_location: "catogories/name.png",
),
),
],
),
);
}}
If you are using small sized images, you can use IconButton().
class HorizontalList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 80.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
IconButton(icon: Image.asset(<name_here(or any other optoin such as Image.network()>)), onPressed: <function_here:fox example clickEvent()>)
],
),
);
}
}
You can also use
icon: Icon(icon: Icons.<icon_name>, color: Colors.<colors_name>)
, if you're using Icons.
And if you're trying to do something else, comment below !