I make responsive UI design in flutter. Then I debuged it in my android emulator. First orientation was portrait and It was a good. Then I changed Orientation and it was a bad UI which isn't expected. If I hot restart or hot reload UI changed that is came expected.
I have to reload or restart always after changing orientation.
Settings already made to autoreloading after save.
How to resolve this issue?
You need to make your UI responsive.
You can do it with the "MediaQuery".
Exemple :
return Scaffold(
appBar: AppBar(
title: Text("Responsive Container"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: MediaQuery.of(context).size.height/ 4,
width: MediaQuery.of(context).size.width/3,
color: Colors.red,
child: Center(child: Text("Hello There !")),
),
],
),
),
);
Yuu said that you are creating responsive UI which means that you are using width and height of the screen to make it responsive. When orientation changes then obviously width increased and height decreased so maybe you need to add check on orientation change. There are many ways of doing that here is the simple one
return Scaffold(
key: scaffoldKey,
body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
orientation == Orientation.portrait ?
doSomeThingifportrait : doSomethingifLandscape;
},
),
);
Related
How can I change the color of a ButtonBar in Flutter? There isn't a color in ButtonBarTheme as far as I can tell. I am trying to do something like this:
return Scaffold(
backgroundColor: Colors.red,
body: Center(child: Text('foo')),
// Somehow get the color of these buttons different to backgroundColor
persistentFooterButtons: _buildTextButtons(),
);
The best I can think of at the moment is to somehow reimplement the persistent footer buttons myself, but that is ugly. The problem seems to be that the Scaffold backgroundColor property is used in multiple places, yet is defined in a single widget. This means I can't wrap it in a Theme and override it piecemeal either, as far as I can tell.
Oh, I might have found an answer. I had been using both the bottomNavigationBar and the persistentFooterButtons. persistentFooterButtons are wrapped in a ButtonBar, which appears to limit your styling options.
bottomNavigationBar is a single widget, though, so you can fiddle with it. I switched to just build my own persistentFooterButtons after all.
(I had to edit this from my app, so it might not be copy/paste ready.)
Widget _buildNavigationBar(BuildContext ctx, Color barColor) {
List<Widget> actions = [
// We need the first element to be big, to push everything to the right.
Expanded(
child: Container(),
),
];
actions.addAll(_buildTextButtons(ctx));
Widget actionContainer = ColoredBox(
color: barColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: actions,
),
),
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
actionContainer,
// I use this to make space for ads.
Container(
color: barColor,
height: 50,
),
],
);
}
return Scaffold(
backgroundColor: Colors.red,
body: Center(child: Text('foo')),
// removed this persistentFooterButtons: _buildTextButtons(),
// and replaced with this:
bottomNavigationBar: _buildNavigationBar(ctx),
);
you can also wrap that ButtonBar into Container and After that, you can make any type of changes to that ButtonBar, like coloring etc
What is the best way to build a "nested bottom sheet" which behaves like this:
https://dribbble.com/shots/14139422-Mobile-banking-app-interactions?
Do I have to use NestedScrollView or CustomScrollView, or a completely different approach?
Update :
This is my result using SlidingUpPanel, but I still have two problems:
When sliding up the panel, the green and red containers stay behind the panel and do not scroll out of view at the top.
In landscape mode the containers are higher than the device, so this approach does not work. I need the panel to be attached to the bottom of the containers, so it is only visible when scrolling down.
Code:
SafeArea(
child: Scaffold(
body: SlidingUpPanel(
minHeight: MediaQuery.of(context).size.height - 479,
maxHeight: MediaQuery.of(context).size.height - 79,
panel: SingleChildScrollView(
child: Column(
children: [Text('Sliding Panel')],
),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 100,
color: Colors.green,
),
Container(
height: 300,
color: Colors.red,
)
],
),
),
),
),
);
Result:
I believe there is a Widget for what you want to do, since there is a widget for everything in Flutter. here is the link of the package
NOTE : you can also set minimum height of the widget when collapsed in order to control how much of the widget you can show when it is collapsed.
And here is the demo :
I have a flare loading animation which takes time to load. Is there a way to pre-cache the flutter animation?
final AssetProvider assetProvider = AssetFlare(bundle: rootBundle, name: 'assets/animations/loop.flr');
cachedActor(assetProvider);
Is this the code to cache the actor?
Then How do I load the cached animation?
From https://github.com/2d-inc/Flare-Flutter/issues/180#issuecomment-550584347
You can use FlareCacheBuilder to help you preload flr files for certain sections of your app
code snippet
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: FlareCacheBuilder(
["assets/Filip.flr"],
builder: (BuildContext context, bool isWarm) {
return !isWarm
? Container(child: Text("Loading..."))
: FlareActor(
"assets/Filip.flr",
alignment: Alignment.center,
fit: BoxFit.contain,
animation: _animationName,
);
},
),
)
],
),
),
);
}
If you want a 'global' load to your .flr files the correct way is using cachedActor just like you posted.
Let's say you previously executed:
final assetProvider = AssetFlare(bundle: rootBundle, name: 'assets/animations/loop.flr');
await cachedActor(assetProvider);
When you try to use the same .flr in a FlareActor it will already try to first load this asset from the same cache you populated before.
In this example the Flare team does exactly this to warmup some files. But notice that they are using an old sintaxe for cachedActor.
You can see at the source code here and here, that when loading the asset it will try at first to load from the cache.
One last thing to finish, FlareCacheBuilder uses the same cachedActor method internally.
Ok so I have got the following bottomNavigationBar working, however it's not clean enough for me and I am hoping there is a better way that someone knows.
Basically I only want to change the body section and not the full page.
However I want to have each section in different classes to keep it neat (ideally new .dart files - as they will all have different functions)
Currently all the page info is inside the body tag
body: PageView(
controller: _myPage,
onPageChanged: (int) {
print('Page Changes to index $int');
},
children: <Widget>[
Center(
child: Container(
child: Text('Empty Body 0'),
),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('images/pic1.jpg'),
Text('images/pic2.jpg'),
Text('images/pic3.jpg'),
],
),
),
Center(
child: Container(
child: Text('Empty Body 2'),
),
),
Center(
child: Container(
child: Text('DRN1 app is made using Google Flutter. While every attempt was made to make sure that this app works on as many devices as possible. We are unable to test every device. If you have issues running this app please let us know'),
),
)
],
physics: NeverScrollableScrollPhysics(), // Comment this if you need to use Swipe.
),
what I would like is something like this.
body: PageView(
controller: _myPage,
onPageChanged: (int) {
print('Page Changes to index $int');
},
children: <Widget>[
home(),
news() , // this is the main body for home
shows(), // this one shows the class shows() which fetches JSON and returns a different body layout
about(), // about text
]
Does anyone know of a better way to do this?
What you can do is create separate Dart files and import them into your main.dart
Then inside your State class define a list which contains the pages that you want to show
List<Widget> _myPage = <Widget>[Page1class(),Page2class(),Page3class()];
After that you can use the below code which looks quite clean. Using the builder() method will allow you to create pages based on the size of _myPage,
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(title: Text('sdf')),
body: PageView.builder(
itemBuilder: (context, position) => _myPage[position],
itemCount: _myPage.length,
),
)
);
Here is a sample program: PageView sample
I'm trying to create a layout similar to Android's build in Contacts App, when creating a new contact (or editing one for that matter), in portrait mode the whole view is scroll-able with the user's avatar placed on top of the contact form in a top-to-bottom layout, when you switch to landscape mode however, the layout changes to a side-by-side layout with the avatar taking up the left half of the screen (full height and non scroll-able) and the contact form to the right fully scroll-able independent of the avatar to the left which stays fixed.
I tried implementing this using the flutter_staggered_grid_view library by setting the crossAxisCount in a StaggeredGridView.countBuilder based on the device orientation this will make it so that I can switch between having only 1 item in the cross axis (in portrait mode where there's a vertical layout) to having 2 items in the cross axis (in landscape mode where there's a horizontal layout).
Unfortunately, even though the StaggeredGridView.countBuilder is nested within a OrientationBuilder whenever I switch orientation, the layout doesn't seem to update as if the crossAxisCount property is not dynamic and can not be updated once set.
body: OrientationBuilder(
builder: (context, or) {
int cac = (or == Orientation.portrait) ? 1 : 2;
return StaggeredGridView.countBuilder(
controller: _listViewScrollController,
itemBuilder: (BuildContext context, int index){
return _view[index];
},
itemCount: 2,
crossAxisCount: cac,
staggeredTileBuilder: (int index) {
return StaggeredTile.fit(cac);
},
scrollDirection: Axis.vertical,
);
},
)
There's the possibility that Grids aren't the right approach to creating such a layout, if that's the case, I'd appreciate if anyone could point me in the right direction.
Thanks in advance.
I think that a better approach would be to play with Row, Column and SingleChildScrollView:
You can try something like this:
Widget buildBody(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
if (orientation == Orientation.portrait) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
_buildAvatar(context, orientation),
_buildFields(context),
],
),
);
} else {
return Row(
children: <Widget>[
_buildAvatar(context, orientation),
Expanded(
child: SingleChildScrollView(
child: _buildFields(context),
),
),
],
);
}
});
}
Widget _buildAvatar(BuildContext context, Orientation orientation) {
return Container(
height: 300.0,
width: orientation == Orientation.landscape ? 300.0 : null,
color: Colors.blue,
child: Center(
child: CircleAvatar(
backgroundColor: Colors.white,
child: Text('RR'),
),
),
);
}
Widget _buildFields(BuildContext context) {
return Container(
height: 800.0,
color: Colors.white,
child: Center(
child: Container(
height: 250.0,
width: 250.0,
color: Colors.red,
),
),
);
}
The buildBody function will create the layout above, and you'll have to replace the content of _buildAvatar and _buildFields to fit your needs.