I have a list view on my homepage where if I clicked on it will move to a new page the first one the theme should be blue, and the next one the theme data should be red.
Like how Chat Customization in Facebook's Messenger works.
You should wrap your Page widget inside a Theme widget. Something like below:
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
backgroundColor: Colors.red,
),
child: Builder(
builder: (context) {
return YourPageWidget(context);
}
),
);
}
There is one thing you should aware of. Using a Builder() to pass the latest context (including your custom theme data in this new context) to the child widget.
If not using Builder, in some cases, YourPageWidget can't get correct theme data with command Theme.of(context)...
For more information, you can reference the document Create unique ThemeData
Related
I'm new to flutter / dart and am just finding my feet.
Been having a play with a really simple test UI. I'm using the Material App and Scaffold widgets and placing a Column widget in the 'body' of the Scaffold widget. It automatically expands to take up the whole screen when I use the inspect widget tool.
This is the code..
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text('bob'),
],
),
);
}
}
If I then nest a Column widget it doesn't expand to take up the vertical space like its parent did, I can change that and take up the space by using the Expanded widget like this..
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text('bob'),
Expanded(
child: Column(children: [
Text('bob2'),
]),
),
],
),
);
}
}
I'm just trying to understand why the parent 'root' Column takes up all the space it can by default but when I remove the Expanded widget the nested Column doesn't?
Must be something simple but I'm not seeing it when I look through the docs and understand.
Many thanks for any help.
This is a layout constraint applied to the root widget by Flutter.
So I have been trying to port my Android and iOS application to Flutter/Dart so I do not have to code everything twice.
I need to display a ListView(that fetches the contents from an array, and then an onClickListener that copies the clicked items content to clipboard.
All fine and good, I have tried two different methods for displaying the ListView from an array and that works fine but I have not gotten the onClickListener to work at all.
Any ideas?
Code like this should work:
class ClipBoardCopier extends StatelessWidget {
#override
Widget build(BuildContext context) {
List<String> data = ['Hello', 'Flutter'];
return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('ListTile #$index'),
onTap: (){
Clipboard.setData(ClipboardData(text: data[index]));
},
);
},
);
}
}
But if Your ListView Item Widget doesn't have Event Listener (onTap, onClick ..) you can wrap it with GestureDetector And you are good to go.
I am getting started with the BLoC pattern but I have a question:
1) Should you use the BloC pattern to determine if the routes should change?
Example: authentication object changes to unauthenticated so the listeners should handle the route changes.
2) Should the BLoC pattern only be used for UI state and handle the route changes on UI changes?
Example: User clicks on login and navigates to the home screen.
I ask this question because I'm facing a problem where I don't have a central navigation management solution.
This code is in my BLoC now:
loggedIn.listen((AuthResponse user) {
currentUserSubject.add(user);
Navigator.pushReplacement(
_context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) {
return HomePage();
},
transitionsBuilder: (context, animation, _, child) {
return new SlideTransition(
child: child,
position: new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
);
},
transitionDuration: Duration(milliseconds: 400),
),
);
}, onError: (error) {
Scaffold.of(_context).showSnackBar(new SnackBar(
content: new Text(error.message),
));
});
What I'm currently doing is:
Register a NavigatorBloc that receive a NavigatorState in the constructor and receive actions to navigate to different pages in your app Ex: GoToHomePageAction, GoToRegisterPageAction, NavigatorActionPop.
In your App widget you register the NavigatorBloc and provide the navigator key attached to the MaterialApp widget.
#override
Widget build(BuildContext context) {
return BlocProvider<NavigatorBloc>(
bloc: NavigatorBloc(navigatorKey: widget.navigatorKey),
child: MaterialApp(
navigatorKey: widget.navigatorKey,
title: 'Medicine Tracker',
theme: ThemeData(
primarySwatch: Colors.red,
scaffoldBackgroundColor: Colors.white
),
home: HomePage(),
),
);
}
Then in side your NavigatorBloc you just check the the action and navigate to the desired page:
class NavigatorBloc extends Bloc<NavigatorAction, dynamic>{
final GlobalKey<NavigatorState> navigatorKey;
NavigatorBloc({this.navigatorKey});
#override
dynamic get initialState => 0;
#override
Stream<dynamic> mapEventToState(NavigatorAction event) async* {
if(event is NavigatorActionPop){
yield navigatorKey.currentState.pop();
}
}
}
Hope it help.
I see there are 2 Yes/No questions there (in contrast to W/H questions), and my answers are yes to both of them. The reason being that with BloC and navigation, you can actually recover the current screen if the app crashes (auto-save state and state recover must be in place, but it's another W/H question), and other nice features of BLoC/event-based state management (history snapshot, time machine, event replay, separate of concerns, testability, etc.)
I ask this question because I'm facing a problem where I don't have a central navigation management solution.
Is there anything I can help with regarding your problem?
'Allo,
My main file is getting up to 1000 lines of code and I can't help but think I could save time by separating the Scaffold into 3 or 4 .dart files. Is this possible?
Between the AppBar and Drawer I'm already up to 500+ lines of code because of all the links and design parameters. I'd like to extricate this code instead of having to scroll through it continually when I'm working on the main body.
Anytime I've tried to take out the drawer and put it in a separate file I get errors everywhere. Problems with 'dynamic' and 'widgets' and return types, etc.
What can I take out the scaffold and reference it to another file?
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],), //end appBar
drawer: new Drawer(......), //end drawer
body: TabBarView(....), //end body
), //end scaffold
I wouldn't mind leaving the main body in this main file but I might also take it out if i had more options. Just want to reduce a 1000+ lines into 2-3 chunks, files of manageable space.
Any ideas?
There is most certainly a way to organize this across different files. In addition to being easier to maintain and test, this may also increase performance if state is involved (because if state changes you have to rebuild the entire tree rather than only rebuilding leaf nodes).
However, this also means that if you have state involved and sprinkled about in your one large build() method then you may have some additional considerations as you organize across files. This is assuming you would create new custom widgets to wrap the various components and you would need to orchestrate the state appropriately.
So with the goal of breaking this build method into different sub Widgets, I recommend you start by breaking it up into functions first:
from:
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],), //end appBar
drawer: new Drawer(......), //end drawer
body: TabBarView(....), //end body
);
}
to:
Widget build(BuildContext context) {
return new Scaffold(
appBar: _appBar(),
drawer: _drawer(),
body: _body(),
);
}
Widget _appBar() {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
Widget _drawer() {
...
}
Widget _body() {
return TabBarView();
}
At this point, you may start to realize what data/state is being passed around as you will have to add parameters to these new helper methods.
If you have a lot of parameter passing (especially on state that changes), you will have other considerations outside the scope of this answer (and we would need to see what state you are actually dealing with).
The next step is to create a new Widget for each of these methods.
From:
Widget _appBar() {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
To:
Widget _appBar(...) {
return MyAppBar(...);
}
class MyAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new AppBar(
bottom: new TabBar(tabs:[.....]),
actions: <Widget> [
new PopupMenuButton<xx>()
],),],);
}
}
You can define MyAppBar in it's own file.
You can also bypass the _appBar(...) method and just construct the new widget in the main build() method (assuming you have no other complex setup):
Widget build(BuildContext context) {
return new Scaffold(
appBar: MyAppBar(),
drawer: MyDrawer(),
body: _body(), // you might want to keep the body in the same file
);
}
Easiest way are methods:
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(),
...
);
}
Widget _buildAppBar() {
return AppBar(...);
}
You can also use separate widgets. The widget in the appBar slot must implement PreferredSizeWidget:
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: MyBody(),
);
}
}
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight); // whatever height you want
#override
Widget build(BuildContext context) {
return AppBar();
}
}
class MyBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text('Hello World'),
),
);
}
}
Of course if place them in a different file, you have to import it:
import 'package:myapp/widgets/some_widget.dart';
I want to use a different theme for my flutter app, depending on the OS it's started on. How can i detect the OS when choosing what theme to apply?
Theme.of(context).platform == TargetPlatform.iOS
doesn't work, because I haven't applied a theme yet...
You can easily override theme by wrapping your view into a new Theme instance with custom properties.
You could do the following :
return new MaterialApp(
// default theme here
theme: new ThemeData(),
builder: (context, child) {
final defaultTheme = Theme.of(context);
if (defaultTheme.platform == TargetPlatform.iOS) {
return new Theme(
data: defaultTheme.copyWith(
primaryColor: Colors.purple
),
child: child,
);
}
return child;
}
);
Which would specify a default theme. And then override primaryColor for IOS.