Chorche
Chorche

Reputation: 85

Storing certain value in Widget build / Flutter

I've a question:

In my Widget build(BuildContext context), I want to store a certain value,

final userName = book.owner

(book is the reference to the certain value from Firestore)

But it's done not in the right way to my lack of knowledge. I'd appreciate if someone could guide through that.

Thank you in advance!

Snippet of my code

class BookView extends StatefulWidget {
  final Book book;
  BookView({Key key, @required this.book}) : super(key: key);

  DatabaseMethods databaseMethods = new DatabaseMethods();
  var userName;
  @override
  _BookViewState createState() => _BookViewState(book);
}

class _BookViewState extends State<BookView> {
  Book book;
  _BookViewState(this.book);
  String userName;
  @override
  void initState() {
    userName = book.owner;

    super.initState();
  }

  // final Book book;
  createChatroomAndStartConversation({var userName}) {
    if (userName != Constants.myName) {
      String roomId = getChatRoomId(userName, Constants.myName);

      List<String> users = [userName, Constants.myName];
      Map<String, dynamic> chatRoomMap = {
        "Users": users,
        "roomId": roomId,
      };

      DatabaseMethods().createChatRoom(roomId, chatRoomMap);
      Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => ConversationScreen(roomId, userName)),
      );
    } else {
      print("You cannot send msg to your self");
    }
  }

  @override
  Widget build(BuildContext context) {
    //widget.book;
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
    ...
                            FlatButton(
                              child: Text(
                                "Get contact with",
                                style: TextStyle(color: Colors.white),
                              ),
                              color: Colors.blue,
                              onPressed: () {
                                createChatroomAndStartConversation(
                                    userName: userName);
 ...
}

Snippet of Value not in range: 1

getChatRoomId(String a, String b) {
  if (a.substring(0, 1).codeUnitAt(0) > b.substring(0, 1).codeUnitAt(0)) {
    return "$b\_$a";
  } else {
    return "$a\_$b";
  }
}

Upvotes: 0

Views: 471

Answers (1)

Alex Altiox
Alex Altiox

Reputation: 154

It's not a good practice to store any data in build() method, because this method is invoked too many times to do the such kind of move. Consider using StatefulWidget to store any state you have in the widget, for the very beginning. When you use this widget, you can define this all in such way:

class YourWidget extends StatefulWidget {

  @override
  _YourWidgetState createState() => _YourWidgetState();
}

class _YourWidgetState extends State<YourWidget> {
  String userName;
  
  @override
  void initState() {
    userName = book.owner;
    
    super.initState()
  }

  @override
  Widget build(BuildContext context) {
    return Container(child: Text(userName),);
  }
}

Here, in initState() you can retrieve value from book and set it to userName. But for more complex and bigger applications, consider using StateManagement solutions and some kind of architectural patterns i.e. Riverpod, Provider, MobX, BLoC.. Because changing the state via setState() method will cause rebuilding whole child widget tree, which could freeze whole UI in complex app.

UPD to 'Snippet of my code':

According to your code, if you are using a 'book' from Widget, not its state - use widget.book, in such way you have access to widget members, because of this you don't need a constructor of state. So, due to these changes, your code might looks like:

class BookView extends StatefulWidget {
  final Book book;
  BookView({Key key, @required this.book}) : super(key: key);

  // You DON'T need this here, because you are retrieving these methods 
  // inside your state via DatabaseMethods constructor
  DatabaseMethods databaseMethods = DatabaseMethods();
  
  @override
  _BookViewState createState() => _BookViewState(book);
}

class _BookViewState extends State<BookView> {
  String userName;

  @override
  void initState() {
    // Using widget.book to retrieve Book object from state's widget
    userName = widget.book.owner;

    super.initState();
  }

  createChatroomAndStartConversation({var userName}) {
    if (userName != Constants.myName) {
      String roomId = getChatRoomId(userName, Constants.myName);
      
      // Also, it's just a recommendation, try to omit local variables types
      // because they are already known with List type (String). Also, this 
      // all is about chatRoomMap
      var users = <String>[userName, Constants.myName];
      final chatRoomMap = <String, dynamic>{
        "Users": users,
        "roomId": roomId,
      };

      DatabaseMethods().createChatRoom(roomId, chatRoomMap);
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ConversationScreen(roomId, userName)),
        );
      } else {
        print("You cannot send msg to your self");
    }
  }
  
  @override
  Widget build(BuildContext context) {
  // your widgets here
  }
}

UPD 2:

Second trouble and issue with 'Snippet of Value not in range: 1'. I could to reproduce it with given value of 'a' as empty string. So, your function invocation is like getChatRoomId('', 'user123'), because of empty 'userName', substring function can't take values from range [0, 1), so exception is raised.

Upvotes: 2

Related Questions