Related
I have an app that shows its content in a SingleChildScrollView. There is Container with a transparent color that I'd like to change the color of to red when the SingleChildScrollView is scrolled to any other position than the start position and then change the color back to transparent when the SingleChildScrollView is scrolled back to its starting position. Code:
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: SingleChildScrollView(
child: Column(
children: [
Stack(...) //This is the top section of the page
],
),
),
),
),
],
),
Container(
color: Colors.transparent, //This is the Color I want to change based on the position of the SingleChildScrollView
height: 120,
child: Column(...)
),
],
),
backgroundColor: Colors.white,
);
}
}
EDIT: I managed to make it work by wrapping the SingleChildScrollView in a NotificationListener and updating the color based on the notification like this:
class _AppState extends State<App> {
Color bannercolor = Colors.transparent;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: NotificationListener<ScrollUpdateNotification>(
onNotification: (scrollEnd) {
final metrics = scrollEnd.metrics;
if (metrics.pixels != 0) {
setState(() {
bannercolor = Colors.white;
});
} else {
setState(() {
bannercolor = Colors.transparent;
});
}
return true;
},
child: SingleChildScrollView(
child: Column(
children: [
Column(...),
],
),
),
),
),
),
],
),
Container(
color: bannercolor,
height: 120,
child: Column(...),
),
],
),
backgroundColor: Colors.white,
);
}
}
You can try listening to the scroll controller offset like this
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
final ScrollController _scrollController = ScrollController ();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
Flexible(
child: ScrollConfiguration(
behavior: RemoveScrollGlow(),
child: SingleChildScrollView(
controller: _scrollController, //add controller here
child: Column(
children: [
Stack(...) //This is the top section of the page
],
),
),
),
),
],
),
AnimatedBuilder(
animation: _scrollController,
builder: (context, _content) {
return Container (
(_scrollController.offset>20)? Colors.blue: Colors.transparent,
height: 120,
child: Column(...)
);
}
),
],
),
backgroundColor: Colors.white,
);
}
}
I'm aware that MediaQuery solutions exist to problems, however, I want to limit the size of my Scaffold so that it can be used for web-based apps as well. Similar to what Instagram has, can anyone help me with it?
Have you tried wrapping your Scaffold in SafeArea with a minimum property of EdgeInsets.all(32.0)?
For me, this recreates your mockup on any screen
Example code:
//...
return SafeArea(
minimum: const EdgeInsets.all(32.0),
child: Scaffold(
//...
),
);
//...
I used sizedboxes to create layers for a single page look. The gridview does not shrink to the size of the window. Instead it activates scrolling. My solution works for chrome web.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.purple,
buttonTheme: const ButtonThemeData(
textTheme:ButtonTextTheme.primary,
buttonColor:Colors.yellow,
)
),
home: Test_SinglePage(),
);
}
}
class DataRecord{
String name;
String number;
DataRecord(this.name,this.number);
}
class Test_SinglePage extends StatefulWidget {
Test_SinglePage({Key? key}) : super(key: key);
#override
State<Test_SinglePage> createState() => _Test_SinglePageState();
}
class _Test_SinglePageState extends State<Test_SinglePage> {
List<DataRecord> lstData=[
DataRecord("A","1"), DataRecord("B","2"), DataRecord("C","3"), DataRecord("D","4"),
DataRecord("E","5"), DataRecord("F","6"), DataRecord("G","7"), DataRecord("H","8"),
DataRecord("I","9"), DataRecord("J","10"), DataRecord("K","11"), DataRecord("L","12"),
DataRecord("M","13"), DataRecord("N","14"), DataRecord("O","15"), DataRecord("P","16"),
DataRecord("Q","17"), DataRecord("R","18"), DataRecord("S","19"), DataRecord("T","20"),
DataRecord("V","21"), DataRecord("X","22"), DataRecord("Y","23"), DataRecord("Z","24"),
];
Widget _dialogBuilder(BuildContext context, String name)
{
return SimpleDialog(
contentPadding:EdgeInsets.zero,
children:[
Container(width:80,height:80,child:
Column(children:[
Text(name),
SizedBox(height:20),
Expanded(child:Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ElevatedButton(onPressed:(){ Navigator.of(context).pop();}, child:
Text("Close"))
],))
])
)]);
}
Widget _itemBuilder(BuildContext context,int index)
{
return
GestureDetector(
onTap:()=>showDialog(context:context,builder:(context)=>_dialogBuilder(context,lstData[index].name)),
child:Container(color:Colors.grey,child:GridTile(child: Center(child:
Column(children:[
Text(lstData[index].name,style:Theme.of(context).textTheme.headline2),
Text(lstData[index].number,style:Theme.of(context).textTheme.headline4)
])
))));
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title:Text("Single Page")),body:
Container(
margin: const EdgeInsets.only(top:20.0, left: 20.0, right: 20.0, bottom:10.0),
child:
Flex(
direction: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child:Row(
mainAxisAlignment: MainAxisAlignment.start,
children:[
FittedBox(
fit:BoxFit.fitHeight,
child:SizedBox(
width:200,
height:200,
child: Image.asset("assets/images/apple.jpg"),
)),
Column(
mainAxisAlignment:MainAxisAlignment.start,
children:[
Row(
children: [
SizedBox(height:100,width:200,child:Container(color:Colors.red,child:Text("reached"))),
SizedBox(height:100,width:200,child:Container(color:Colors.blue,child:Text("reached2"))),
SizedBox(height:100,width:200,child:Container(color:Colors.green,child:Text("reached3")))
],),
Row(children: [
SizedBox(width:600, child:ElevatedButton(
onPressed:(){
},child:Text("Press Me")))],)
])
])),
Expanded(child:SizedBox(
height:400,
width:MediaQuery.of(context).size.width,child:
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: lstData.length,
itemBuilder: _itemBuilder
)))],)
,));
}
}
Im building a personal website and everytime I pop back from Projects or Blog Page to my home page the Material App changes from the title i initially put it to the name of the folder carpet of the project. I still don't understand why this happens, so any help would be greatly appreciated.
Note: I'm using the Fluro package for my navigation route.
Image representation of how the MaterialApp Title changes
Blog Page =>Home Page
blog_page.dart
import 'package:flutter/material.dart';
class BlogPage extends StatefulWidget {
#override
_BlogPageState createState() => _BlogPageState();
}
class _BlogPageState extends State<BlogPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: WillPopScope(
onWillPop: () async => true,
child: Center(
child: FractionallySizedBox(
widthFactor: 0.8,
child: FittedBox(
fit: BoxFit.fill,
child: Center(
child: Text(
'Hello Stranger!',
style: Theme.of(context).textTheme.headline1,
),
),
),
),
),
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:webapp/router.dart';
void main() {
FluroRouter.setupRouter();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Personal Website',
initialRoute: 'home',
onGenerateRoute: FluroRouter.router.generator,
);
}
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'package:webapp/widgets/social_media.dart';
import 'package:webapp/widgets/wave_body.dart';
import 'package:webapp/widgets/custom_button_border.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
HomePage() {
timeDilation = 1.0;
}
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
Size size = new Size(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height,
);
return DesktopLayout();
}
}
class DesktopLayout extends StatelessWidget {
const DesktopLayout({
Key key,
#required this.size,
}) : super(key: key);
final Size size;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(47, 66, 83, 1.0),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
flex: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Flexible(
flex: 1,
child: Container(),
),
Flexible(
flex: 1,
child: ProfessionalSocialMedia(),
),
Flexible(
flex: 1,
child: Container(),
),
Flexible(
flex: 1,
child: PersonalSocialMedia(),
),
Flexible(
flex: 1,
child: Container(),
),
],
),
),
SizedBox(
height: 15.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomButtonBorder(
stringText: 'Projects',
size: size,
onPressed: () {
Navigator.pushNamed(context, 'project');
},
),
SizedBox(
width: 50.0,
),
CustomButtonBorder(
stringText: 'Blog',
size: size,
onPressed: () {
Navigator.pushNamed(context, 'blog');
},
)
],
),
Stack(
children: [
WaveBody(
size: size,
xOffset: 0,
yOffset: 0,
color: Color.fromRGBO(21, 160, 132, 1.0),
),
],
),
],
),
);
}
}
The Fluro package could be easily be using the Title Widget which changes the Tab name.
That Widget takes a "title (String)" and a "color (Color)" and will update the name over the Tab.
If you're only using Flutter Web you can take advantage of the http class and also replace your url to match your title:
#override
void initState() {
super.initState();
window.history.pushState(null, 'Blog Page', 'blog-page');
}
That will update your URL to "https://my-url.com/blog-page" and adding the Title Widget your tab will say "Blog Page" as well.
#override
Widget build(BuildContext context) {
return Material(
child: Title(
title: 'Blog Page',
color: Colors.white,
child: Container(),
),
);
}
If by any reason you also need Mobile, change your: import 'dart:html'; for the library "universal_html": https://pub.dev/packages/universal_html
I am new to Flutter/Dart but I am trying to incorporate a ListView into a project that I am working on but I cannot get it to work. I have tried it at least 12 different ways and it is still not working. I know the issue has something to do with the way I am sizing (or not sizing) the ListView/ListTiles. I have tried adding heights to SizedBoxes, heights to Containers, widths to columns, Expanded, Flexible etc. Nothing has worked. I am not posting the error messages because with all of the solutions I have tried they have all essentially been related to either the height or width of the ListView/ListTile exceeding the limitations of the screen. Below are the latest code snippet I have tried. I know there are several topics on SO that have addressed this but I have not been able to get any of them to work. My ListView should only contain 3 tiles so perhaps a ListView.Builder would be better. I am open to any suggestions or advice. Thanks in advance for the help!
class _HotelFormState extends State<HotelForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hotel Form'),
),
body: Column(
children: <Widget>[
SizedBox(height: 20.0),
Row(
children: <Widget>[
TripDates(),
],
),
SizedBox(height: 20.0),
Column(
children: <Widget>[
RoomCounts(),
],
),
],
),
);
}
}
RoomCounts Class
class RoomCounts extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
height: 100.0,
child: new ListView(
children: new List.generate(3, (i) => new ListTileItem(title: "$i",)),
),
),
);
}
}
ListTile Class
class _ListTileItemState extends State<ListTileItem> {
//Some lists are here
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Flexible(
child: ListTile(
leading: Icon(widget.icons),
title: new Text(widget.title),
trailing: new Row(
children: <Widget>[
_itemCount != 0
? new IconButton(icon: new Icon(Icons.remove), onPressed: () => setState(() => _itemCount--),)
: new Container(),
new Text(_itemCount.toString()),
new IconButton(icon: new Icon(Icons.add), onPressed: () => setState(() => _itemCount++))
],
),
),
),
],
);
}
}
You can copy paste run full code below
You do not need Scaffold in RoomCounts
You do not need Column in _ListTileItemState
You need SizedBox in trailing of ListTile
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HotelForm(),
);
}
}
class HotelForm extends StatefulWidget {
#override
_HotelFormState createState() => _HotelFormState();
}
class _HotelFormState extends State<HotelForm> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hotel Form'),
),
body: Column(
children: <Widget>[
SizedBox(height: 20.0),
/*Row(
children: <Widget>[
TripDates(),
],
),*/
SizedBox(height: 20.0),
Column(
children: <Widget>[
RoomCounts(),
],
),
],
),
);
}
}
class RoomCounts extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 100.0,
child: ListView(
children: List.generate(
3,
(i) => ListTileItem(
title: "$i",
)),
),
);
}
}
class ListTileItem extends StatefulWidget {
final String title;
final IconData icons;
ListTileItem({this.title, this.icons});
#override
_ListTileItemState createState() => _ListTileItemState();
}
class _ListTileItemState extends State<ListTileItem> {
int _itemCount = 0;
#override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(widget.icons),
title: Text(widget.title),
trailing: SizedBox(
width: 200,
child: Row(
children: <Widget>[
_itemCount != 0
? IconButton(
icon: Icon(Icons.remove),
onPressed: () => setState(() => _itemCount--),
)
: Container(),
Text(_itemCount.toString()),
IconButton(
icon: Icon(Icons.add),
onPressed: () => setState(() => _itemCount++))
],
),
),
);
}
}
I need to display images of several different sizes in a ListView.
When the image is larger than screen.width, I'd like it to shrink to fit width.
But when the image is shorter, I'd like it to keep its original size.
How can I do it? Thanks in advance.
I tried putting Image inside Flex, but couldn't "stop" the small one to expand.
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:firebase_database/firebase_database.dart';
void main() => runApp(MyApp());
const _imagesDir = "images";
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image List',
theme: ThemeData(primarySwatch: Colors.blue,),
home: MyListPage(title: 'Image List Page'),
);
}
}
class MyListPage extends StatefulWidget {
MyListPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyListPageState createState() => _MyListPageState();
}
class _MyListPageState extends State<MyListPage> {
Widget build1(BuildContext context, AsyncSnapshot snapshot) {
Widget _tileImagem(BuildContext context, String imageName) {
imageName = _imagesDir + "/"+ imageName;
return Padding(padding:EdgeInsets.all(2.0),
child: Flex(
direction: Axis.vertical,
children: <Widget>[
Image.asset(imageName),
]
),
);
}
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(40.0),
child: AppBar(
title: Row(
children: <Widget> [
Padding(padding: EdgeInsets.only(right: 20.0),),
Text( 'Duda'),
]),
)
),
body: ListView(
shrinkWrap: true,
children: <Widget>[
Container(),
_tileImagem(context, 'flutter_big_medium.png'),
Container(), //My App have some different widgets
Container(),
Container(), //I kept them here just as place holder
Container(),
Container(),
Container(),
Container(),
Container(),
Divider(),
TileTexts(),
Divider(),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () { },
child: Icon(Icons.skip_next),
),
);
}
#override
Widget build(BuildContext context) {
return new FutureBuilder(
future:
FirebaseDatabase.instance.reference()
.child('Testing')
.once(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState){
case ConnectionState.done: return build1(context, snapshot);
case ConnectionState.waiting: return CircularProgressIndicator();
default:
if (snapshot.hasError) {
return Text("hasError: ${snapshot.error}");
} else {
return Text("${snapshot.data}");
}
}
}
);
}
}
class TileTexts extends StatefulWidget {
TileTexts() : super();
#override
_TileTextsState createState() => _TileTextsState();
}
class _TileTextsState extends State<TileTexts> {
#override
void initState() {
super.initState();
}
Widget text1(String title, String imageName, TextStyle style) {
return Expanded(
child:Container(
margin: const EdgeInsets.only(left: 10.0),
child: Column(
children: <Widget>[
Html(data: title,
useRichText: true,
defaultTextStyle: style,
),
((imageName == null))
? Container()
: Image.asset(_imagesDir + "/"+imageName),
]
),
),
);
}
Widget _tileDetail(BuildContext context, String imageName) {
return Container(
padding: EdgeInsets.fromLTRB(5.0,0.0,10.0,0.0),
child: Row(
children: <Widget>[
Material(
shape: RoundedRectangleBorder(borderRadius:BorderRadius.circular(22.0) ),
clipBehavior: Clip.antiAlias,
child: MaterialButton(
child: Text('X'),
color: Theme.of(context).accentColor,
elevation: 8.0,
height: 36.0,
minWidth: 36.0,
onPressed: () {
//
},
),
),
text1('<body>veja a imagem</body>', imageName, Theme.of(context).textTheme.caption),
],
),
);
}
//_TileTexts
#override
Widget build(BuildContext context) {
print('_TileTexts build');
return Column(
children: <Widget>[
_tileDetail(context, 'flutter_med_medium.png'),
Divider(),
_tileDetail(context, 'flutter_med_medium.png'),
Divider(),
_tileDetail(context, 'flutter_med_medium.png'),
],
);
}
}
Create an method,getTitleImage(imageName), that returns Flex if image is bigger then screen-with, else return the image inside an container or in other widget of choice.
....
return Padding(padding:EdgeInsets.all(2.0),
child: getTitleImage(imageName)
),
);
....
Here is some other tips and tricks using Flex
Please check the doc, it says:
The heights of the leading and trailing widgets are constrained according to the Material spec. An exception is made for one-line ListTiles for accessibility. Please see the example below to see how to adhere to both Material spec and accessibility requirements.
after reading docs, you should achieve what you want :)