I'm new to flutter. I want to make an app that shows a page selected by BottomNavigationBar.
But when I'm trying to run the app, it throws an Exception. The following is Error Log.
════════ Exception caught by widgets library
The following NoSuchMethodError was thrown building Builder:
The method '_debugTypesAreRight' was called on null.
Receiver: null
Tried calling: _debugTypesAreRight(Instance of 'MainPages')
The relevant error-causing widget was:
MaterialApp file:///C:/Users/jango/AndroidStudioProjects/study_and_statistic/lib/main.dart:49:14
When the exception was thrown, this was the stack:
0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
1 new StatefulElement.<anonymous closure> (package:flutter/src/widgets/framework.dart:4309:19)
2 new StatefulElement (package:flutter/src/widgets/framework.dart:4320:6)
3 StatefulWidget.createElement (package:flutter/src/widgets/framework.dart:809:38)
4 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3189:40)
and my code is here
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class AppConfig {
static double width;
static double height;
static double blockSize;
static double blockSizeVertical;
static double statusBarHeight;
static double getAppbarHeight(){
double ratioHeight = blockSizeVertical*9;
return (ratioHeight>60)? 60 : ratioHeight;
}
static double getGap(){
double ratioGap = width/20;
return (ratioGap>30)? 30 : ratioGap;
}
static double getFontsize_content(){
double ratioSize = (blockSize>blockSizeVertical)?blockSizeVertical*6:blockSize*6;
return (ratioSize > 18)? 18: ratioSize;
}
static double getFontsize_appBar(){
double ratioSize = (blockSize>blockSizeVertical)?blockSizeVertical*7:blockSize*7;
return (ratioSize > 20)? 20: ratioSize;
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
FocusScope.of(context).unfocus();
},
child: MaterialApp(
title: 'STUDY',
theme: ThemeData(
fontFamily: 'NanumBarunGothic',
primaryColor: Color(0XFF5dc19b),
),
home: MainPages() //Here is the problem, maybe..
)
);
}
}
PreferredSize DailyAppBar(){
//My customAppBar
//AppConfig used here
}
class SubjectListTile extends StatelessWidget{
//My custom ListTile
//AppConfig used here
}
class SubjectList extends StatefulWidget{
#override
State createState() => SubjectListState();
}
class SubjectListState extends State<SubjectList>{
//My custom Listview
}
class MainPages extends StatefulWidget{
const MainPages({ Key key }) : super(key: key);
#override
_MainPagesState createState() {
_MainPagesState();
}
}
class _MainPagesState extends State<MainPages>{
int _currentIndex = 0;
final List<Widget> pages = [
SubjectList(),
StudyPage(),
StaticPage(),
];
void init_AppConfig(BuildContext context){
AppConfig.width = MediaQuery.of(context).size.width;
AppConfig.height = MediaQuery.of(context).size.height;
AppConfig.blockSize = AppConfig.width / 100;
AppConfig.blockSizeVertical = AppConfig.height / 100;
AppConfig.statusBarHeight = MediaQuery.of(context).padding.top;
double width = AppConfig.width;
double height = AppConfig.height;
print('width: $width');
print('height: $height');
}
void _onItemTapped(int index){
setState((){
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
init_AppConfig(context);
return Scaffold(
appBar: DailyAppBar(),
body : pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onItemTapped,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.check_box),
title: Text('오늘의 공부'),
),
BottomNavigationBarItem(
icon: Icon(Icons.chrome_reader_mode),
title: Text('집중모드'),
),
BottomNavigationBarItem(
icon: Icon(Icons.show_chart),
title: Text('기록'),
),
],
),
);
}
}
class StaticPage extends StatelessWidget{ //Not impleted yet
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child:Text("StaticPage")),
);
}
}
class StudyPage extends StatelessWidget{ //Not impleted yet
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child:Text("StudyPage")),
);
}
}
In MyApp, MainPages() is called as home of MaterialApp. At that time, it throws an Exception.
In MainPagesState class, build() function initializes App Configuration first.
And then it builds Scaffold Widget, which includes DailyAppBar(my custom Appbar), pages[_currentIndex], bottomNavigationBar. Daily AppBar and pages[0] use AppConfig Data.
Is there a mistake when using init_appConfig or bottomNavigationBar?
Appconfig, SubjectListTile, SubjectList and State, DailyAppBar worked well when I put SubjectList() in body of Scaffold directly.
You have missed the return statement.
#override
_MainPagesState createState() {
return _MainPagesState();
}
or just use arrow function
#override
_MainPagesState createState() => _MainPagesState();
Related
I've made some test widgets to illustrate a point that I'm having difficulty with in a much more complicated widget.
I have the following widget:
class TestListWidget extends StatefulWidget {
Widget child;
TestListWidget({Widget child}) {
this.child = child;
}
#override
State<StatefulWidget> createState() {
return TestListWidgetState();
}
}
class TestListWidgetState extends State<TestListWidget>
{
Widget child;
int buttonCount = 0;
#override initState() {
child = widget.child;
}
_clickedCountButton()
{
setState(() {
buttonCount++;
});
}
#override
Widget build(BuildContext context) {
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
child
]);
}
}
The above widget is being used inside the following widget:
class TestList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new TestListState();
}
}
class TestListState extends State<TestList> {
String _testStr = "not clicked";
_clickButton()
{
setState(() {
_testStr = "CLICKED";
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
primarySwatch: Colors.blue
),
home:Scaffold(
body : testListWidget(child: Row(children: [
Text(_testStr),
ElevatedButton(onPressed: _clickButton, child: Text("Click me!"))
]))));
}
}
The issue I'm having is when the "Click me!" button is clicked, the function is called, but the text on the screen is not updated to "CLICKED!". The "Update Count" button works as intended though.
If I make the TestListWidget a stateless widget (and remove the update count button functionality) then the "Click Me!" button works as expected. Is there any way to make a child widget rebuild when passed to a stateful widget?
Your problem is quite simple. You have two variables, one is TestListWidget.child, we'll call it stateful widget's child. The second is TestListWidgetState.child, we'll call it state's child.
You make state's child to be equal to stateful widget's child on initState, but initState only runs when you first create a state, so updating the stateful widget's child will not update the state's child because initState won't run again.
To fix this, I believe you can just completely remove state's child, and use widget.child instead:
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
widget.child
]);
Full example:
import 'package:flutter/material.dart';
void main() => runApp(TestList());
class TestListWidget extends StatefulWidget {
Widget child;
TestListWidget({required this.child});
#override
State<StatefulWidget> createState() {
return TestListWidgetState();
}
}
class TestListWidgetState extends State<TestListWidget>
{
int buttonCount = 0;
_clickedCountButton()
{
setState(() {
buttonCount++;
});
}
#override
Widget build(BuildContext context) {
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
widget.child
]);
}
}
class TestList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new TestListState();
}
}
class TestListState extends State<TestList> {
String _testStr = "not clicked";
_clickButton()
{
setState(() {
_testStr = "CLICKED";
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
primarySwatch: Colors.blue
),
home:Scaffold(
body : TestListWidget(child: Row(children: [
Text(_testStr),
ElevatedButton(onPressed: _clickButton, child: Text("Click me!"))
]))));
}
}
I am trying to change the body of a screen in flutter based on whether there is any record in my table.
import 'package:flutter/material.dart';
import '../widgets/appDrawer.dart';
import '../widgets/emptyHomeScreen.dart';
import '../data/database.dart';
import '../widgets/filledHomeScreen.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final dbHelper = DBProvider.db;
int count;
_MyHomePageState() {
dbHelper.getCount().then((val) => setState(() {
count = val;
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Files'),
),
drawer: AppDrawer(),
floatingActionButton: buildFloatingActionButton(context),
body: Center(
child: count > 0 ? FilledHomeScreen() : EmptyHomeScreen(),
),
);
}
}
When I tried to run my app, error was thrown for just a fraction of second and then the app just worked fine.
How do I fix it?
The error thrown was:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building MyHomePage(dirty, state: _MyHomePageState#4d44c):
The method '>' was called on null.
Receiver: null
Tried calling: >(0)
The code for the function getCount() is:
Future<int> getCount() async {
//database connection
final Database db = await database;
var x = await db.rawQuery('SELECT COUNT (*) from documents');
int count = Sqflite.firstIntValue(x);
return count;
}
Is there any way of doing this by converting the statefulwidget to statelesswidget?
class _MyHomePageState extends State<MyHomePage> {
final dbHelper = DBProvider.db;
int count;
_MyHomePageState() {
dbHelper.getCount().then((val) => setState(() {
count = val;
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Scans'),
),
drawer: AppDrawer(),
floatingActionButton: buildFloatingActionButton(context),
body: Center(
child: count != null && count > 0 ? FilledHomeScreen() : EmptyHomeScreen(),
),
);
}
}
I am pretty new to Flutter. I want to develop a Application with a Bottom Navigation Bar. But I don't really know what is the best way of Navigation. I made a custom Version of this Tutorial:
https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386
What I did until now was:
Created a Main Widget with a Scaffold with a BottomNavigationBar
Everytime the index is changing I change the child Property of the Scaffold
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainWidget()
);
}
}
/// This is the stateful widget that the main application instantiates.
class MainWidget extends StatefulWidget {
#override
_MainWidgetState createState() => _MainWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MainWidgetState extends State<MainWidget> {
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
index: _selectedIndex,
children: allDestinations.map<Widget>((Destination destination) {
return DestinationView(destination: destination);
}).toList(),
)
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.blue,
currentIndex: _selectedIndex,
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
items: allDestinations.map((Destination destination) {
return BottomNavigationBarItem(
icon: Icon(destination.icon),
label: destination.title
);
}).toList(),
)
);
}
}
enum DestinationType {
Page1,
Page2,
Page3,
Page4,
Page5
}
class Destination {
const Destination(this.type, this.title, this.icon);
final DestinationType type;
final String title;
final IconData icon;
}
const List<Destination> allDestinations = <Destination>[
Destination(DestinationType.Page1, 'Page1', Icons.lightbulb),
Destination(DestinationType.Page2, 'Page2', Icons.search),
Destination(DestinationType.Page3, 'Page3', Icons.attach_money),
Destination(DestinationType.Page4, 'Page4', Icons.calendar_today_outlined),
Destination(DestinationType.Page5, 'Page5', Icons.settings)
];
I return a DestinationView where I am checking what Destination should be built.
class DestinationView extends StatefulWidget {
const DestinationView({Key key, this.destination}) : super(key: key);
final Destination destination;
#override
_DestinationViewState createState() => _DestinationViewState();
}
class _DestinationViewState extends State<DestinationView> {
#override
Widget build(BuildContext context) {
switch (widget.destination.type) {
case DestinationType.Page1:
return Page1Destination(destination: widget.destination);
case DestinationType.Page2:
return Page2Destination(destination: widget.destination);
case DestinationType.Page3:
return Page3();
case DestinationType.Page4:
return Page4();
case DestinationType.Page5:
return Page5();
}
}
}
If I have a Part where I want to have Navigation in only this Part, I create a Navigator and define routes:
class Page1Destination extends StatefulWidget {
const Page1Destination({Key key, this.destination}) : super(key: key);
final Destination destination;
#override
_Page1DestinationState createState() => _Page1DestinationState();
}
class _Page1DestinationState extends State<Page1Destination> {
#override
Widget build(BuildContext context) {
return Navigator(
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
settings: settings,
builder: (BuildContext context) {
switch(settings.name) {
case '/':
return Page1Home(destination: widget.destination);
case '/list':
return Page1List();
case '/settings':
return Page1Settings(destination: widget.destination);
}
},
);
},
);
}
}
Inside these widgets I use Navigator.pushNamed and so on.
If the Tab/Page is only one widget. I only return a normal widget without any routes.
But if want to call a Widget in Page 1 with the route /list and a parameter from another page. I don't know how to do that.
I'm pretty sure there is a better way of handling that kind of Navigation.
So maybe one of you knows how I can create a better Navigation-Handler.
i am trying to integrate a Webview in a Test app using the Package flutter_webview_plugin
my goal is to hide a bottomNavigationBar when the User scroll up in the Webview and show it, when the user scroll down.
in the mentioned Package there is a listner to listen to vertical Schroll changes :
final flutterWebviewPlugin = new FlutterWebviewPlugin();
flutterWebviewPlugin.onScrollYChanged.listen((double offsetY) { // latest offset value in vertical scroll
// compare vertical scroll changes here with old value
});
the offsetY value, is the current value, but how can't i get the old value, to compare it with the new value ? any idea ?
ok, i have implemented a solution to this.
i have defined a variable oldOffset = 0.0 and in the method trackOffsetChange i checked if oldOffset value smaller than the currentOffset value. if it's the case then oldOffset get the value of currentOffset and with setState i rebuild the widget to hide the BottomNavBar, else show the BottomNavBar.
here is the whole code of the test App, if someone new like me, is interested to see the source code:
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.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: WebInApp(),
);
}
}
class WebInApp extends StatefulWidget {
#override
_WebInAppState createState() => _WebInAppState();
}
class _WebInAppState extends State<WebInApp> {
bool _isVisible = true;
double oldOffset = 0.0;
final flutterWebOlugin = FlutterWebviewPlugin();
void trackOffsetChange(double currentOffset) {
print('current Offset --->> $currentOffset');
print('old Offset --->> $oldOffset');
if (oldOffset < currentOffset) {
print('old Offset In -- IF --->> $oldOffset');
oldOffset = currentOffset;
setState(() {
_isVisible = false;
});
} else {
setState(() {
_isVisible = true;
});
print('old Offset In -- ESLE --->> $oldOffset');
oldOffset = currentOffset;
}
}
#override
void initState() {
super.initState();
flutterWebOlugin.onScrollYChanged.listen((double yOffset) {
trackOffsetChange(yOffset);
});
}
#override
Widget build(BuildContext context) {
return WebviewScaffold(
url: "https://play.google.com/store/apps",
// hidden: true,
appBar: AppBar(
title: Text('WebView'),
),
bottomNavigationBar: AnimatedContainer(
duration: Duration(microseconds: 300),
height: _isVisible ? 60.0 : 0.0,
child: bottomNav(),
),
);
}
}
class bottomNav extends StatelessWidget {
const bottomNav({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.forward),
title: Text('back'),
),
BottomNavigationBarItem(
icon: Icon(Icons.arrow_back),
title: Text('forward'),
),
],
);
}
}
Good day. I've watched a video about Flutter's InheritedModel and got interested on it. Unfortunately, I can't seems to make it work properly.
Summary: Need help how to properly implement InheritedModel.
Expected Code Output: Widget CountText should not be updated when updating count parameter in CountModel.
Actual Code Output: CountText still updates (I think this is due to that the parent widget is a StatefulWidget)
Details
I am trying to implement a Counter app using InheritedModel. Code below is my code
import 'package:flutter/material.dart';
class CountModel extends InheritedModel<String> {
final int count;
CountModel({ this.count, child }) : super(child: child);
#override
bool updateShouldNotify(CountModel oldWidget) {
if (oldWidget.count != count) {
return true;
}
return false;
}
#override
bool updateShouldNotifyDependent(InheritedModel<String> oldWidget, Set<String> dependencies) {
if (dependencies.contains('counter')) {
return true;
}
return false;
}
static CountModel of(BuildContext context, String aspect) {
return InheritedModel.inheritFrom<CountModel>(context, aspect: aspect);
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Counter',
theme: Theme.of(context),
home: Counter(),
);
}
}
class Counter extends StatefulWidget {
#override
CounterState createState() => CounterState();
}
class CounterState extends State<Counter> {
int count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// title: Text("Counter"),
),
body: CountModel(
count: count,
child: CounterText()
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
++count;
});
},
child: Icon(Icons.add),
),
);
}
}
class CounterText extends StatelessWidget {
#override
Widget build(BuildContext context) {
CountModel model = CountModel.of(context, 'test');
return Text('Count: ${model.count}');
}
}
I have a CountModel as InheritedModel and a CountText widget which consumes the data from the CountModel. As you can see in the implementation of the CountText, it pass test when getting the CountModel. In my understanding, it should not be updated when the count value is updated in the CountModel. Unfortunately, this does not happen.
In short, you should use const.
Add const to CounterText constructor
class CounterText extends StatelessWidget {
const CounterText();
...
}
and use const when you create instance of CounterText() (const CounterText())
class CounterState extends State<Counter> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: CountModel(..., child: const CounterText()),
...
);
}
}
And voila 🎉
I have described why this is happening here in details